Getting the Vercel AI SDK talking to n4n.ai takes less than an hour if you follow a disciplined sequence. This vercel ai sdk setup checklist n4n.ai walks through the eight steps that separate a working prototype from a production-ready integration. Each item includes the minimal code you need and the gotchas that bite teams the first time.
1. Install the core packages and the OpenAI-compatible provider
Start with a clean dependency set. The Vercel AI SDK v4+ splits the core from provider adapters, so you only pull what you use.
npm i ai @ai-sdk/openai zod
The @ai-sdk/openai package works unchanged against n4n.ai because n4n.ai exposes an OpenAI-compatible /v1 endpoint. No community wrapper required. If you’re on pnpm or yarn, the same spec applies — just swap the install command.
Create a single module that exports your configured client. This keeps the base URL and API key out of every route handler.
// lib/n4n-client.ts
import { createOpenAI } from '@ai-sdk/openai';
export const n4n = createOpenAI({
baseURL: 'https://api.n4n.ai/v1',
apiKey: process.env.N4N_API_KEY,
});
Set N4N_API_KEY in your environment. Never hardcode it. Vercel projects pull from .env.local locally and from the dashboard in production.
2. Verify the model catalog before you code
n4n.ai routes to 240+ models behind one endpoint. The model ID you pass (for example, anthropic/claude-3.5-sonnet or meta-llama/llama-3.1-70b-instruct) is a routing directive, not a provider-specific name. Call the /v1/models endpoint once during onboarding to see what’s currently available and cache the list.
// scripts/list-models.ts
import { n4n } from '../lib/n4n-client';
const models = await n4n.models.list();
console.log(models.data.map(m => m.id).join('\n'));
Run this script whenever you add a new model to your feature flags. The catalog changes as providers add or deprecate models; hardcoding IDs in your repo creates silent failures.
3. Configure routing directives and fallback behavior
One of the strongest reasons to use n4n.ai is automatic fallback when a provider is rate-limited or degraded. You control this with the provider and fallback fields in the request body — or by omitting them entirely and letting the gateway decide.
// app/api/chat/route.ts
import { streamText } from 'ai';
import { n4n } from '@/lib/n4n-client';
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: n4n('anthropic/claude-3.5-sonnet'),
messages,
// Optional: pin a provider or enable explicit fallback
providerOptions: {
n4n: {
provider: 'anthropic', // optional hint
fallback: ['openai', 'google'], // ordered fallback chain
},
},
});
return result.toDataStreamResponse();
}
If you omit providerOptions, n4n.ai applies its default routing policy: lowest latency healthy provider for the requested model. Explicit fallback chains are useful when you have compliance requirements (e.g., “never send this workload to provider X”).
4. Implement streaming with the Data Stream protocol
Vercel AI SDK v4 uses a binary-friendly Data Stream protocol by default. It works over standard fetch and plays nicely with edge runtimes. The client-side useChat hook consumes it without extra parsing.
// components/Chat.tsx
'use client';
import { useChat } from 'ai/react';
export function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/chat',
streamProtocol: 'data', // default in v4
});
return (
<div>
{messages.map(m => (
<div key={m.id} className={m.role}>
{m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">Send</button>
</form>
</div>
);
}
On the server, streamText returns a DataStreamResponse that sets Content-Type: text/plain; charset=utf-8 and the x-vercel-ai-data-stream header. No manual chunk formatting required.
5. Add tool calling with Zod schemas
Tool calling is where the SDK pays for itself. Define schemas with Zod, pass them to streamText, and the model returns structured tool-call parts that your route handler executes.
// app/api/chat/route.ts (extended)
import { z } from 'zod';
const getWeather = {
parameters: z.object({
latitude: z.number(),
longitude: z.number(),
}),
execute: async ({ latitude, longitude }) => {
const res = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true`
);
return res.json();
},
};
const result = await streamText({
model: n4n('anthropic/claude-3.5-sonnet'),
messages,
tools: { getWeather },
maxSteps: 5, // allow multi-step tool loops
});
The maxSteps parameter lets the model call tools, receive results, and call again — essential for agents that need to chain lookups. Keep execute functions pure and side-effect-free; the SDK handles the loop.
6. Wire up observability from day one
Don’t ship without logs. The SDK exposes onFinish and onError callbacks that receive the full StreamTextResult — including token counts, tool calls, latency, and the model ID that actually served the request (after fallback).
const result = await streamText({
model: n4n('anthropic/claude-3.5-sonnet'),
messages,
tools: { getWeather },
onFinish: async ({ response, usage, finishReason }) => {
await fetch('https://api.your-observability.com/ingest', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: response.modelId,
promptTokens: usage.promptTokens,
completionTokens: usage.completionTokens,
finishReason,
timestamp: new Date().toISOString(),
}),
});
},
onError: ({ error }) => {
console.error('[chat] stream error', error);
// Send to Sentry, Datadog, etc.
},
});
n4n.ai forwards provider cache-control hints and per-token usage in the response headers. Capture x-n4n-provider, x-n4n-model, and x-n4n-usage if you need provider-level cost allocation.
7. Handle rate limits, timeouts, and partial failures
The gateway returns standard HTTP codes: 429 for rate limits, 502/503 for upstream degradation, 400 for bad requests. Wrap the stream in a try/catch and surface actionable messages to the client.
export async function POST(req: Request) {
try {
const { messages } = await req.json();
const result = await streamText({
model: n4n('anthropic/claude-3.5-sonnet'),
messages,
abortSignal: req.signal, // propagate client disconnect
});
return result.toDataStreamResponse();
} catch (err) {
if (err instanceof Error && err.name === 'AbortError') {
return new Response('Client disconnected', { status: 499 });
}
if (err?.status === 429) {
return new Response('Rate limited — try again in a few seconds', { status: 429 });
}
console.error('[chat] fatal', err);
return new Response('Internal error', { status: 500 });
}
}
Pass abortSignal: req.signal so the gateway cancels upstream work when the user navigates away. This saves tokens and keeps your bill honest.
8. Test with at least three models before launch
Model behavior varies wildly. A prompt that works on Claude 3.5 Sonnet may hallucinate on Llama 3.1 70B or refuse on Gemini 1.5 Pro. Build a tiny eval harness that runs your top 20 prompts against three models and diffs the outputs.
// scripts/eval.ts
import { n4n } from '../lib/n4n-client';
import { generateText } from 'ai';
const models = [
'anthropic/claude-3.5-sonnet',
'meta-llama/llama-3.1-70b-instruct',
'google/gemini-1.5-pro',
];
const prompts = [
'Summarize this PR diff in two sentences...',
'Write a SQL query for...',
// ...
];
for (const modelId of models) {
console.log(`\n=== ${modelId} ===`);
for (const prompt of prompts) {
const { text } = await generateText({
model: n4n(modelId),
prompt,
temperature: 0.2,
});
console.log(`→ ${text.slice(0, 120)}...`);
}
}
Commit the baseline outputs. When n4n.ai adds a new model or a provider updates weights, re-run and review diffs. This catches regressions before users do.
Summary checklist
| Step | Artifact | Verified? |
|---|---|---|
| 1. Install & client module | lib/n4n-client.ts |
☐ |
| 2. Model catalog script | scripts/list-models.ts |
☐ |
| 3. Routing & fallback config | providerOptions in route |
☐ |
| 4. Streaming endpoint | streamText + toDataStreamResponse |
☐ |
| 5. Tool definitions | Zod schemas + execute fns |
☐ |
| 6. Observability hooks | onFinish / onError |
☐ |
| 7. Error handling | try/catch + abortSignal | ☐ |
| 8. Multi-model eval | scripts/eval.ts + baselines |
☐ |
Tick each box before you merge to main. The vercel ai sdk setup checklist n4n.ai isn’t ceremony — it’s the difference between a demo that works on your machine and an API that survives a traffic spike at 2 AM.