The AI API Layer: Your Product's Brain
Adding AI capabilities to your product is no longer optional — it is a competitive expectation. Users who experience an intelligent, AI-powered product and then return to a static, rule-based alternative feel the difference immediately.
This chapter walks you from your first AI API call through to the advanced patterns that power production AI products.
---
Level 1: Your First AI API Call
Every AI API call follows the same structure regardless of provider. Let's use the Gemini API (free tier available):
This is the foundation. Every more complex use case is a variation on this pattern.
---
Level 2: System Prompts and Conversation History
A system prompt tells the model how to behave. Conversation history lets the model respond in context.
---
Level 3: Streaming Responses
Users expect to see AI output appear progressively, not wait 10 seconds for the full response. Streaming is essential for any user-facing AI feature.
```javascript // Next.js API route with streaming export async function POST(request) { const { prompt } = await request.json(); const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); const stream = await client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: prompt }], stream: true, // Enable streaming
Get advanced blueprint resources
Enter your email to receive technical blueprints, checklists, and visual configurations accompanying this chapter.
No spam. Unsubscribe any time.
});
// Return a ReadableStream to the client return new Response( new ReadableStream({ async start(controller) { for await (const chunk of stream) { const text = chunk.choices[0]?.delta?.content || ''; if (text) controller.enqueue(new TextEncoder().encode(text)); } controller.close(); }, }), { headers: { 'Content-Type': 'text/plain; charset=utf-8' } } ); }
javascript // Client-side: reading the stream progressively const response = await fetch('/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt }), });const reader = response.body.getReader(); const decoder = new TextDecoder(); let result = '';
while (true) { const { done, value } = await reader.read(); if (done) break; result += decoder.decode(value); setOutput(result); // Update React state progressively }
javascript const response = await client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: 'Extract the key information from this job posting: ' + jobText }], response_format: { type: 'json_object' }, // Forces JSON output });const extracted = JSON.parse(response.choices[0].message.content); // { title: "...", salary: "...", requirements: [...], location: "..." }
javascript const tools = [{ type: 'function', function: { name: 'search_products', description: 'Search the product catalog for items matching the query', parameters: { type: 'object', properties: { query: { type: 'string', description: 'The search query' }, max_price: { type: 'number', description: 'Maximum price filter' }, }, required: ['query'], }, }, }];// First call — model may decide to call a function const response = await client.chat.completions.create({ model: 'gpt-4o', messages: [{ role: 'user', content: 'Find me running shoes under $80' }], tools, tool_choice: 'auto', });
if (response.choices[0].finish_reason === 'tool_calls') { const toolCall = response.choices[0].message.tool_calls[0]; const args = JSON.parse(toolCall.function.arguments); // Actually run your search function const results = await searchProducts(args.query, args.max_price); // Second call — give the model the function result const finalResponse = await client.chat.completions.create({ model: 'gpt-4o', messages: [ { role: 'user', content: 'Find me running shoes under $80' }, response.choices[0].message, { role: 'tool', tool_call_id: toolCall.id, content: JSON.stringify(results) }, ], }); }
javascript async function processDocument(document) { // Step 1: Cheap model extracts structure const structure = await geminiFlash.extract(document); // Step 2: Best model handles nuanced analysis const analysis = await claudeFable5.analyze(structure); // Step 3: Fast model generates the summary const summary = await geminiFlash.summarize(analysis); return { structure, analysis, summary }; } ```This pattern delivers frontier-quality results at 60-70% of the cost of routing everything through the most expensive model.