# Streaming OpenAI output from a Next.js App Router route handler
Route handlers return `Response` objects. To stream, build a `ReadableStream`
and pump SSE frames into it.
## Handler shape
```js
export const runtime = "nodejs";
export async function POST(req) {
const { prompt } = await req.json();
const openai = new OpenAI();
const upstream = await openai.responses.create({ model: "...", input: prompt, stream: true });
const stream = new ReadableStream({
async start(controller) {
const enc = new TextEncoder();
const send = (obj) => controller.enqueue(enc.encode("data: " + JSON.stringify(obj) + "\n\n"));
for await (const event of upstream) {
if (event.type === "response.output_text.delta") send({ delta: event.delta });
if (event.type === "response.completed") break;
}
controller.close();
},
});
return new Response(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" } });
}
```
## Traps
- `export const runtime = "nodejs"`. The default may work for plain fetch, but the
OpenAI SDK's streaming and any websocket usage need the Node runtime. Pin it.
- No `req`/`res` Express objects exist here. Do not port Express SSE code
line-by-line; there is nothing to call `writeHead` on.
- The OpenAI client runs server-side only. Never import the client or the API key
into a client component; the route handler is the boundary.
## Check before you ship
- `fetch()` the route and read the reader: first frame should arrive in seconds.
- View page source / bundle: the API key must not appear in any client chunk.