Learn how to use the ObjectQL AI Agent programmatically in your Node.js applications. This guide covers the TypeScript API for building custom development tools, web UIs, and automation with AI-powered generation and validation.
The AI Agent is available in @objectql/core package and can be used to:
- Generate applications from natural language
- Validate metadata programmatically
- Build interactive application builders
- Create AI-powered development tools
The AI Agent is part of the core package:
npm install @objectql/coreimport { ObjectQLAgent } from '@objectql/core';
const agent = new ObjectQLAgent({
apiKey: process.env.OPENAI_API_KEY!,
model: process.env.OPENAI_MODEL || 'gpt-4', // optional, default: 'gpt-4'
temperature: 0.7, // optional, default: 0.7
language: 'en' // optional, default: 'en'
});const result = await agent.generateApp({
description: 'A task management system with projects and tasks',
type: 'complete', // 'basic' | 'complete' | 'custom'
maxTokens: 4000 // optional
});
if (result.success) {
console.log(`Generated ${result.files.length} files:`);
for (const file of result.files) {
console.log(`- ${file.filename} (${file.type})`);
// Write to disk
fs.writeFileSync(
path.join('./src', file.filename),
file.content
);
}
} else {
console.error('Errors:', result.errors);
}const yamlContent = fs.readFileSync('./user.object.yml', 'utf8');
const validation = await agent.validateMetadata({
metadata: yamlContent,
filename: 'user.object.yml',
checkBusinessLogic: true,
checkPerformance: true,
checkSecurity: true
});
if (!validation.valid) {
console.log('Errors:');
validation.errors.forEach(err => {
console.log(` - ${err.message} (${err.location})`);
});
}
if (validation.warnings.length > 0) {
console.log('Warnings:');
validation.warnings.forEach(warn => {
console.log(` - ${warn.message}`);
if (warn.suggestion) {
console.log(` Suggestion: ${warn.suggestion}`);
}
});
}Build applications through multi-turn conversation:
let conversationHistory: ConversationMessage[] = [];
let currentApp: GenerateAppResult | undefined;
// First turn
const result1 = await agent.generateConversational({
message: 'Create a blog system with posts and comments',
conversationHistory,
currentApp
});
conversationHistory = result1.conversationHistory;
currentApp = result1;
console.log('Generated files:', result1.files.map(f => f.filename));
console.log('Suggestions:', result1.suggestions);
// Second turn - refine based on user feedback
const result2 = await agent.generateConversational({
message: 'Add tags to posts and categories for organization',
conversationHistory,
currentApp
});
conversationHistory = result2.conversationHistory;
currentApp = result2;
console.log('Updated files:', result2.files.map(f => f.filename));Iteratively improve metadata based on feedback:
const initialMetadata = `
label: User
fields:
name:
type: string # Wrong - should be 'text'
email:
type: text
`;
const refined = await agent.refineMetadata(
initialMetadata,
'Fix field types and add email validation',
2 // number of refinement iterations
);
console.log('Refined metadata:', refined.files[0].content);interface AgentConfig {
/** OpenAI API key */
apiKey: string;
/** OpenAI model to use (default: gpt-4) */
model?: string;
/** Temperature for generation (0-1, default: 0.7) */
temperature?: number;
/** Preferred language for messages (default: en) */
language?: string;
}interface GenerateAppOptions {
/** Natural language description of the application */
description: string;
/** Type of generation: basic (minimal), complete (comprehensive), or custom */
type?: 'basic' | 'complete' | 'custom';
/** Maximum tokens for generation */
maxTokens?: number;
}interface GenerateAppResult {
/** Whether generation was successful */
success: boolean;
/** Generated metadata files */
files: Array<{
filename: string;
content: string;
type: 'object' | 'validation' | 'form' | 'view' | 'page' |
'menu' | 'action' | 'hook' | 'permission' | 'workflow' |
'report' | 'data' | 'application' | 'typescript' | 'test' | 'other';
}>;
/** Any errors encountered */
errors?: string[];
/** AI model response (raw) */
rawResponse?: string;
}interface ValidateMetadataOptions {
/** Metadata content (YAML string or parsed object) */
metadata: string | any;
/** Filename (for context) */
filename?: string;
/** Whether to check business logic consistency */
checkBusinessLogic?: boolean;
/** Whether to check performance considerations */
checkPerformance?: boolean;
/** Whether to check security issues */
checkSecurity?: boolean;
}interface ValidateMetadataResult {
/** Whether validation passed (no errors) */
valid: boolean;
/** Errors found */
errors: Array<{
message: string;
location?: string;
code?: string;
}>;
/** Warnings found */
warnings: Array<{
message: string;
location?: string;
suggestion?: string;
}>;
/** Informational messages */
info: Array<{
message: string;
location?: string;
}>;
}interface ConversationMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}interface ConversationalGenerateOptions {
/** Initial description or follow-up request */
message: string;
/** Previous conversation history */
conversationHistory?: ConversationMessage[];
/** Current application state (already generated files) */
currentApp?: GenerateAppResult;
}interface ConversationalGenerateResult extends GenerateAppResult {
/** Updated conversation history */
conversationHistory: ConversationMessage[];
/** Suggested next steps or questions */
suggestions?: string[];
}import express from 'express';
import { ObjectQLAgent } from '@objectql/core';
const app = express();
const agent = new ObjectQLAgent({ apiKey: process.env.OPENAI_API_KEY! });
// Store conversations per session
const sessions = new Map<string, ConversationMessage[]>();
app.post('/api/generate', async (req, res) => {
const { sessionId, message, currentApp } = req.body;
const conversationHistory = sessions.get(sessionId) || [];
const result = await agent.generateConversational({
message,
conversationHistory,
currentApp
});
sessions.set(sessionId, result.conversationHistory);
res.json(result);
});
app.listen(3000);import { ObjectQLAgent } from '@objectql/core';
import { ObjectQL } from '@objectql/core';
async function generateAndTest(description: string) {
const agent = new ObjectQLAgent({ apiKey: process.env.OPENAI_API_KEY! });
// Generate app
const result = await agent.generateApp({
description,
type: 'complete'
});
if (!result.success) {
throw new Error('Generation failed');
}
// Write files
for (const file of result.files) {
fs.writeFileSync(`./test-app/${file.filename}`, file.content);
}
// Validate all metadata
for (const file of result.files.filter(f => f.filename.endsWith('.yml'))) {
const validation = await agent.validateMetadata({
metadata: file.content,
filename: file.filename,
checkBusinessLogic: true,
checkSecurity: true
});
if (!validation.valid) {
console.error(`Validation failed for ${file.filename}:`, validation.errors);
}
}
// Start ObjectQL instance and test
const objectql = new ObjectQL({
metadataPath: './test-app',
driver: 'sqlite',
connection: { filename: ':memory:' }
});
await objectql.connect();
// Run tests
// ...
await objectql.disconnect();
}// In your CI pipeline
import { ObjectQLAgent } from '@objectql/core';
async function validateAllMetadata(metadataDir: string): Promise<boolean> {
const agent = new ObjectQLAgent({ apiKey: process.env.OPENAI_API_KEY! });
const files = glob.sync(`${metadataDir}/**/*.yml`);
let hasErrors = false;
for (const file of files) {
const content = fs.readFileSync(file, 'utf8');
const result = await agent.validateMetadata({
metadata: content,
filename: path.basename(file),
checkBusinessLogic: true,
checkSecurity: true,
checkPerformance: true
});
if (!result.valid) {
console.error(`❌ ${file}:`);
result.errors.forEach(e => console.error(` ${e.message}`));
hasErrors = true;
}
result.warnings.forEach(w => {
console.warn(`⚠️ ${file}: ${w.message}`);
});
}
return !hasErrors;
}
// In GitHub Actions workflow
const success = await validateAllMetadata('./src/metadata');
process.exit(success ? 0 : 1);class CustomAppGenerator {
private agent: ObjectQLAgent;
constructor(apiKey: string) {
this.agent = new ObjectQLAgent({ apiKey });
}
async generateFromTemplate(
template: string,
variables: Record<string, string>
): Promise<GenerateAppResult> {
// Replace variables in template
let description = template;
for (const [key, value] of Object.entries(variables)) {
description = description.replace(`{{${key}}}`, value);
}
// Generate
const result = await this.agent.generateApp({
description,
type: 'complete'
});
// Post-process files
if (result.success) {
result.files = result.files.map(file => ({
...file,
content: this.postProcess(file.content, variables)
}));
}
return result;
}
private postProcess(content: string, variables: Record<string, string>): string {
// Custom post-processing logic
return content;
}
}
// Usage
const generator = new CustomAppGenerator(process.env.OPENAI_API_KEY!);
const result = await generator.generateFromTemplate(
'A {{industry}} management system with {{entities}}',
{
industry: 'healthcare',
entities: 'patients, appointments, and medical records'
}
);Always handle errors when using the AI Agent:
try {
const result = await agent.generateApp({
description: 'My application'
});
if (!result.success) {
// Handle generation failure
console.error('Generation failed:', result.errors);
// You might want to retry or provide feedback to user
if (result.rawResponse) {
console.log('Raw response:', result.rawResponse);
}
}
} catch (error) {
// Handle API errors (network, auth, etc.)
if (error instanceof Error) {
console.error('API error:', error.message);
}
}-
API Key Security: Never hardcode API keys. Use environment variables.
-
Rate Limiting: Implement rate limiting when exposing the agent in a web API.
-
Caching: Cache generation results to avoid redundant API calls.
-
Validation: Always validate generated metadata before using in production.
-
Error Recovery: Implement retry logic with exponential backoff for API failures.
-
Type Safety: Use TypeScript for type safety with the agent API.
-
Testing: Test generated applications thoroughly before deployment.
- See CLI Usage for command-line tools
- Read Generating Apps for prompting best practices
- Check Building Apps for using ObjectQL in AI applications