# 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.