Home/
Part XIII — Expert Mode: Systems, Agents, and Automation/39. Prompt Engineering for Experts (The Real Stuff)/39.2 Prompt "APIs": stable interfaces between app and model
39.2 Prompt "APIs": stable interfaces between app and model
Overview and links for this section of the guide.
On this page
The Concept
A Prompt API is a stable contract between your application and the model. Like a REST API, it has:
- Input schema: What data goes in
- Output schema: What format comes out
- Behavior contract: What the model should do
- Version: For backwards compatibility
Defining a Prompt API
// prompt-api/code-review.ts
import { z } from 'zod';
// Input schema
export const CodeReviewInput = z.object({
diff: z.string().describe('The git diff to review'),
context: z.object({
repository: z.string(),
baseBranch: z.string(),
prTitle: z.string()
}),
focus: z.enum(['security', 'performance', 'general']).optional()
});
// Output schema
export const CodeReviewOutput = z.object({
summary: z.string().describe('One-line summary of the review'),
issues: z.array(z.object({
severity: z.enum(['critical', 'high', 'medium', 'low', 'nitpick']),
file: z.string(),
line: z.number().optional(),
message: z.string(),
suggestion: z.string().optional()
})),
approved: z.boolean(),
confidence: z.number().min(0).max(1)
});
// The prompt API
export const codeReviewAPI = {
name: 'code-review',
version: '2.1.0',
inputSchema: CodeReviewInput,
outputSchema: CodeReviewOutput,
buildPrompt(input: z.infer): string {
return `
${GLOBAL_RULES}
${CODE_REVIEW_RULES}
Review the following diff:
\`\`\`diff
${input.diff}
\`\`\`
Context:
- Repository: ${input.context.repository}
- Base branch: ${input.context.baseBranch}
- PR title: ${input.context.prTitle}
${input.focus ? `- Focus area: ${input.focus}` : ''}
Respond with JSON matching this schema:
${JSON.stringify(CodeReviewOutput.shape, null, 2)}
`;
},
async execute(input: unknown): Promise> {
const validated = CodeReviewInput.parse(input);
const prompt = this.buildPrompt(validated);
const response = await model.generateContent({
contents: [{ role: 'user', parts: [{ text: prompt }] }],
generationConfig: { responseMimeType: 'application/json' }
});
return CodeReviewOutput.parse(JSON.parse(response.response.text()));
}
};
Versioning
// prompt-api/registry.ts
interface PromptAPIVersion {
version: string;
deprecated?: boolean;
deprecationDate?: Date;
buildPrompt: (input: any) => string;
}
export class PromptAPIRegistry {
private apis = new Map>();
register(name: string, api: PromptAPIVersion) {
if (!this.apis.has(name)) {
this.apis.set(name, new Map());
}
this.apis.get(name)!.set(api.version, api);
}
get(name: string, version?: string): PromptAPIVersion {
const versions = this.apis.get(name);
if (!versions) throw new Error(`Unknown API: ${name}`);
if (version) {
const exact = versions.get(version);
if (exact) return exact;
throw new Error(`Version ${version} not found for ${name}`);
}
// Return latest non-deprecated
const sorted = [...versions.entries()]
.filter(([_, v]) => !v.deprecated)
.sort((a, b) => b[0].localeCompare(a[0], undefined, { numeric: true }));
return sorted[0][1];
}
}
// Usage
registry.register('code-review', {
version: '2.0.0',
deprecated: true,
deprecationDate: new Date('2024-03-01'),
buildPrompt: oldBuildPrompt
});
registry.register('code-review', {
version: '2.1.0',
buildPrompt: newBuildPrompt
});
Testing
// prompt-api/code-review.test.ts
describe('CodeReviewAPI v2.1.0', () => {
const api = codeReviewAPI;
it('validates input schema', () => {
expect(() => api.inputSchema.parse({})).toThrow();
expect(() => api.inputSchema.parse({
diff: 'test',
context: { repository: 'test', baseBranch: 'main', prTitle: 'Fix bug' }
})).not.toThrow();
});
it('produces valid output', async () => {
const input = {
diff: `@@ -1,3 +1,3 @@\n-old\n+new`,
context: { repository: 'test', baseBranch: 'main', prTitle: 'Test' }
};
const output = await api.execute(input);
expect(() => api.outputSchema.parse(output)).not.toThrow();
});
it('detects security issues', async () => {
const input = {
diff: `+const query = \`SELECT * FROM users WHERE id = \${userId}\``,
context: { repository: 'test', baseBranch: 'main', prTitle: 'Add query' },
focus: 'security' as const
};
const output = await api.execute(input);
expect(output.issues.some(i => i.severity === 'critical')).toBe(true);
});
});
Schema Validation is Essential
Always validate both input and output with a schema library like Zod. If the model returns invalid JSON or missing fields, you'll catch it immediately instead of causing downstream bugs.