Browse documentation
FrameworksVercel AI SDK

Vercel AI SDK

Integrate nRouter with Vercel AI SDK in Next.js and React apps. Stream completions, utilize guardrails, and track costs while routing to any major AI model.

Last updated

The Vercel AI SDK provides a unified TypeScript interface for building interactive AI applications across Next.js, React, Svelte, and Node.js. Through its official @ai-sdk/openai provider adapter, you can point the Vercel AI SDK directly at nRouter (https://api.nrouter.ai/v1) without rewriting application logic.

By routing Vercel AI SDK requests through nRouter, full-stack applications immediately benefit from enterprise gateway capabilities: pre-execution prompt injection defense, server-side content moderation, semantic response caching, transparent model fallbacks, and real-time cost attribution down to the exact fraction of a cent.

Prerequisites & Installation

The Vercel AI SDK requires Node.js 18 or higher. It is optimized for the Next.js App Router but works in any modern JavaScript runtime supporting web standard fetch and streams.

Install the core SDK along with the OpenAI provider adapter and Zod for schema validation:

npm install ai @ai-sdk/openai zod

If you use pnpm, yarn, or bun:

pnpm add ai @ai-sdk/openai zod
# or
yarn add ai @ai-sdk/openai zod
# or
bun add ai @ai-sdk/openai zod

Setup & Configuration

Store your nRouter virtual API key in your project's .env.local file. Never prefix your server-side API key with NEXT_PUBLIC_ to avoid exposing credentials to client web browsers:

# .env.local
NROUTER_API_KEY="sk-nrouter-your-virtual-key"

Initializing the Provider

Create a shared provider instance that points to the nRouter gateway:

// lib/ai.ts
import { createOpenAI } from "@ai-sdk/openai";

export const nrouter = createOpenAI({
  apiKey: process.env.NROUTER_API_KEY,
  baseURL: "https://api.nrouter.ai/v1",
});

Configuration Parameters

The createOpenAI factory accepts several parameters to tune network behavior and gateway directives:

ParameterTypeDefaultDescription
baseURLstringhttps://api.nrouter.ai/v1nRouter unified API endpoint. Must end with /v1.
apiKeystringprocess.env.OPENAI_API_KEYYour nRouter virtual key (sk-nrouter-...).
headersRecord<string, string>{}Custom headers sent on every request (e.g. x-nr-routing).
fetchtypeof fetchglobalThis.fetchCustom fetch implementation for corporate proxies or HTTP agents.
// lib/ai-advanced.ts
import { createOpenAI } from "@ai-sdk/openai";

export const nrouter = createOpenAI({
  apiKey: process.env.NROUTER_API_KEY,
  baseURL: "https://api.nrouter.ai/v1",
  headers: {
    "x-nr-routing": "latency",
  },
  fetch: (url, options) => {
    return fetch(url, {
      ...options,
      // Configure keepalive and connection reuse
      keepalive: true,
    });
  },
});

Implementation Patterns

1. Generating Text (Route Handler)

For synchronous, non-streaming server tasks like summarization, classification, or background jobs:

// app/api/generate/route.ts
import { NextResponse } from "next/server";
import { generateText } from "ai";
import { nrouter } from "@/lib/ai";

export async function POST(req: Request) {
  try {
    const { prompt } = await req.json();

    const { text, usage } = await generateText({
      model: nrouter("claude-sonnet-4-5-20250929"),
      prompt,
      maxTokens: 1024,
    });

    return NextResponse.json({
      text,
      tokens: {
        prompt: usage.promptTokens,
        completion: usage.completionTokens,
      },
    });
  } catch (error: any) {
    return NextResponse.json(
      { error: error.message || "Generation failed" },
      { status: 500 }
    );
  }
}

2. Real-time Streaming with streamText

Stream tokens to web clients with low time-to-first-token (TTFT) using Next.js App Router:

// app/api/chat/route.ts
import { streamText } from "ai";
import { nrouter } from "@/lib/ai";

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: nrouter("gpt-5.4-mini"),
    messages,
  });

  return result.toDataStreamResponse();
}

3. Client Interface with useChat

Connect your React user interface to the streaming route handler using the useChat hook:

// app/chat/page.tsx
"use client";

import { useChat } from "ai/react";

export default function ChatPage() {
  const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
    api: "/api/chat",
  });

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
      <div className="flex-1 overflow-y-auto space-y-4 mb-4">
        {messages.map((m) => (
          <div
            key={m.id}
            className={`p-3 rounded-lg ${
              m.role === "user" ? "bg-blue-600 text-white ml-auto" : "bg-zinc-800 text-zinc-100"
            } max-w-[80%]`}
          >
            <div className="text-xs opacity-70 mb-1">{m.role}</div>
            <div className="whitespace-pre-wrap">{m.content}</div>
          </div>
        ))}
        {isLoading && <div className="text-sm text-zinc-400">Streaming tokens...</div>}
        {error && <div className="text-sm text-red-500">Error: {error.message}</div>}
      </div>

      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask anything..."
          className="flex-1 px-4 py-2 border rounded-md dark:bg-zinc-900"
        />
        <button
          type="submit"
          disabled={isLoading}
          className="px-4 py-2 bg-blue-600 text-white rounded-md disabled:opacity-50"
        >
          Send
        </button>
      </form>
    </div>
  );
}

4. Tool Calling with Server-side Guardrail Defense

Define client tools using Zod. nRouter evaluates safety guardrails before the tool executes:

// app/api/tools/route.ts
import { generateText, tool } from "ai";
import { z } from "zod";
import { nrouter } from "@/lib/ai";

export async function POST(req: Request) {
  const { query } = await req.json();

  const { text } = await generateText({
    model: nrouter("gpt-5.5"),
    prompt: query,
    tools: {
      getWeather: tool({
        description: "Retrieve real-time weather conditions for a given city.",
        parameters: z.object({
          city: z.string().describe("City name"),
          unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
        }),
        execute: async ({ city, unit }) => {
          return { temperature: unit === "celsius" ? 21 : 70, condition: "Sunny", city };
        },
      }),
    },
  });

  return Response.json({ text });
}

Production Best Practices

Deterministic Routing & Fallback Headers

Ensure high availability and cost control by setting routing preferences:

import { generateText } from "ai";
import { nrouter } from "@/lib/ai";

const { text } = await generateText({
  // Specify fallback chain: tries primary model, falls back to secondary
  model: nrouter("gpt-5.4-mini,claude-haiku-4-5-20251001"),
  prompt: "Summarize this technical document...",
  headers: {
    "x-nr-routing": "latency",
  },
});
  • x-nr-routing: latency: Directs requests to the fastest operational cloud endpoint.
  • x-nr-routing: cost: Selects the lowest-cost provider endpoint.
  • Model Fallback List: In case of upstream provider outages, nRouter automatically cascades down your comma-separated list of models.

Per-Request Gateway Overrides

Pass nRouter-specific flags via the body option on generateText or streamText:

const result = await streamText({
  model: nrouter("gpt-5.5"),
  messages,
  body: {
    nrouter_prompt_template_id: "tmpl_support_triage_v2",
    nrouter_prompt_variables: { locale: "en-US", tier: "enterprise" },
    nrouter_cache: true,
  },
});

Runtime Selection in Next.js

You can deploy route handlers to either the Edge Runtime or Node.js Runtime. When operating in high-concurrency environments, Edge Runtime delivers minimal cold starts:

// app/api/chat/route.ts
export const runtime = "edge"; // or 'nodejs' (default)

Troubleshooting & Error Handling

nRouter returns standard HTTP status codes. Inspect the error response when calls fail:

Common Error Codes

StatusError CodeRoot CauseSolution
400guardrail_blockedPrompt injection, PII, or forbidden keywords detectedInspect incoming messages; adjust guardrail thresholds in the nRouter dashboard.
401authentication_errorMissing or invalid NROUTER_API_KEYVerify .env.local contains a valid virtual key starting with sk-nrouter-.
402insufficient_creditsZero credit balance or virtual key budget reachedRefill organization credits or increase key spend ceilings.
429rate_limit_exceededAccount RPM/TPM ceiling hitExponential backoff on retries; request quota increases in settings.
500 / 503service_unavailableUpstream model provider outageAdd secondary fallback models in the model string.

Error Catching in Route Handlers

// app/api/chat/route.ts
import { streamText } from "ai";
import { nrouter } from "@/lib/ai";

export async function POST(req: Request) {
  try {
    const { messages } = await req.json();

    const result = streamText({
      model: nrouter("gpt-5.4-mini"),
      messages,
      onError({ error }) {
        console.error("Stream generation error:", error);
      },
    });

    return result.toDataStreamResponse();
  } catch (error: any) {
    if (error.status === 400 && error.message?.includes("guardrail")) {
      return new Response("Request flagged by organization safety policy", { status: 400 });
    }
    if (error.status === 402) {
      return new Response("Billing budget exceeded. Please contact admin.", { status: 402 });
    }
    return new Response(error.message || "Internal server error", { status: 500 });
  }
}

Next Steps

Was this page helpful?