streamText on the route
On the server side, streamText is the heart of every route in this workshop. It takes a model and messages, returns a streaming result, and that result knows how to serialize itself into a UI message stream the client can read.
import { streamText, convertToModelMessages } from 'ai';
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { mistral } from '@ai-sdk/mistral';
export const maxDuration = 30;
const provider = process.env.CHAT_PROVIDER ?? 'openrouter';
export async function POST(req: Request) {
const { messages } = await req.json();
const model =
provider === 'mistral' && process.env.MISTRAL_API_KEY
? mistral('mistral-large-latest')
: createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY ?? '',
}).chat(process.env.OPENROUTER_MODEL ?? 'google/gemini-2.5-flash-lite');
const result = streamText({
model,
messages: convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
}The whole route fits on one screen. convertToModelMessages maps the UI message shape from the hook onto the shape the provider expects, and toUIMessageStreamResponse sends the result back in the streaming protocol useChat understands.
Three small pieces, one big behavior
How convertToModelMessages, streamText, and toUIMessageStreamResponse compose.
On Vercel, maxDuration tells the platform how long the serverless function can run before it is killed. Streaming responses often take longer than the default, so we bump it to thirty seconds for chat and sixty for the RAG routes that do extra work.
Fill in the blanks: Complete the route
Loading practice…
Quiz: Quiz
Loading practice…