Tutorial

TypeScript & Next.js AI SDK

Build reactive, full-stack AI applications with Next.js App Router, the Vercel AI SDK, and InfinityRouter with complete TypeScript type safety.

Installation

Install the official AI SDK and OpenAI compatibility layer:

shellterminal
npm install ai @ai-sdk/openai zod

1. Next.js API Route Handler (`app/api/chat/route.ts`)

Configure the OpenAI provider with your custom InfinityRouter baseURL and stream responses directly to the browser:

typescriptapp/api/chat/route.ts
import { createOpenAI } from "@ai-sdk/openai";
import { streamText } from "ai";

// 1. Initialize InfinityRouter OpenAI provider
const infinity = createOpenAI({
  baseURL: process.env.INFINITY_BASE_URL || "https://infinityrouter.qd.je/v1",
  apiKey: process.env.INFINITY_API_KEY,
});

export const maxDuration = 30;

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

  // 2. Stream completion using canonical model name
  const result = streamText({
    model: infinity("claude-sonnet-5"),
    messages,
    system: "You are an expert AI engineer assisting with architecture reviews.",
  });

  // 3. Return streaming data response
  return result.toDataStreamResponse();
}

2. Client-side Chat UI Component (`app/chat.tsx`)

Use the useChat hook to handle message state, streaming tokens, auto-scrolling, and form submissions:

tsxapp/chat.tsx
"use client";

import { useChat } from "ai/react";

export function ChatComponent() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();

  return (
    <div className="chat-container">
      <div className="messages-list">
        {messages.map((m) => (
          <div key={m.id} className={`message ${m.role}`}>
            <strong>{m.role === "user" ? "You: " : "Assistant: "}</strong>
            <span>{m.content}</span>
          </div>
        ))}
      </div>

      <form onSubmit={handleSubmit} className="chat-input-form">
        <input
          value={input}
          placeholder="Ask a question..."
          onChange={handleInputChange}
          disabled={isLoading}
        />
        <button type="submit" disabled={isLoading || !input.trim()}>
          {isLoading ? "Thinking..." : "Send"}
        </button>
      </form>
    </div>
  );
}

3. Server-Side Node.js Script with Typed Tools

If you are building background workers or CLI tools in Node.js, use the official openai package:

typescriptworker.ts
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: process.env.INFINITY_BASE_URL || "https://infinityrouter.qd.je/v1",
  apiKey: process.env.INFINITY_API_KEY,
});

async function runWorker() {
  const stream = await client.chat.completions.create({
    model: "claude-sonnet-5",
    messages: [{ role: "user", content: "Provide a quick performance checklist for Redis clustering." }],
    stream: true,
  });

  for await (const chunk of stream) {
    process.stdout.write(chunk.choices[0]?.delta?.content || "");
  }
  console.log("\nDone.");
}

runWorker().catch(console.error);