Vercel AI SDK · Next.js + Node + Edge

Vercel AI SDK + Leanroute

The Vercel AI SDK's @ai-sdk/openai provider accepts a baseURL via its createOpenAI factory. Build the factory once, import it wherever you use generateText, streamText, useChat, or generateObject — every call routes through Leanroute.

1. Install + get a gateway key

npm install ai @ai-sdk/openai
# or: pnpm add ai @ai-sdk/openai
# or: bun add ai @ai-sdk/openai

From /dashboard/keys create a key labeled "vercel-ai-sdk". Add it to .env.local as LEANROUTE_API_KEY=gw_live_....

2. Build the provider factory

One file, import it across your app. Never inline the key in checked-in code — keep it in an env var so the browser bundle can't leak it.

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

// One factory, use across your whole Next.js app. Wire the API key
// via env var — never inline the gw_live_* string in checked-in code.
export const leanroute = createOpenAI({
  baseURL: "https://api.leanroute.dev/v1",
  apiKey: process.env.LEANROUTE_API_KEY!,
});

3. Non-streaming: generateText

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

const { text } = await generateText({
  model: leanroute("anthropic/claude-sonnet-4-6"),
  prompt: "Explain server-side rendering in one sentence.",
});

console.log(text);

4. Streaming route handler (Next.js App Router)

Runs on the Edge runtime for lowest first-token latency. Server-sent events are forwarded from the upstream provider chunk-by-chunk with no intermediate buffering.

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

export const runtime = "edge";

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

  const result = streamText({
    model: leanroute("anthropic/claude-sonnet-4-6"),
    messages,
  });

  return result.toDataStreamResponse();
}

5. useChat React hook

Client component that talks to your /api/chat route above. The hook handles message state, streaming updates, and form input for you.

// app/chat/page.tsx
"use client";
import { useChat } from "ai/react";

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

  return (
    <div>
      {messages.map((m) => (
        <p key={m.id}>
          <strong>{m.role}:</strong> {m.content}
        </p>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
      </form>
    </div>
  );
}

6. Structured output: generateObject

Zod-validated JSON output from any model that supports function calling upstream. Leanroute forwards the schema to the provider unchanged.

import { generateObject } from "ai";
import { z } from "zod";
import { leanroute } from "@/lib/leanroute";

const { object } = await generateObject({
  model: leanroute("openai/gpt-4o"),
  schema: z.object({
    summary: z.string(),
    tags: z.array(z.string()),
  }),
  prompt: "Summarize: LLM gateways route API calls across providers.",
});

console.log(object.tags);   // e.g. ["llm", "infrastructure"]

Troubleshooting

  • 401 unauthenticated: issue a new key at /dashboard/keys.
  • 400 unknown_model: the SDK passes the model string as-is. Use the canonical provider/model form (e.g. openai/gpt-4o, not gpt-4o).
  • Key visible in browser DevTools: you wired the factory in a client component. Move it to a route handler (server-side) and call your own /api/chat from the client via useChat.
  • CORS errors on client-side fetches: you probably tried to call api.leanroute.dev from the browser directly. Always proxy through your own route handler.