Streaming de respostas com IA: SSE, fetch e WebSockets

May 18, 2026 (5d ago)

Problema

Bufferizar resposta inteira do LLM antes de mostrar na UI aumenta TTFB percebido. WebSocket aberto “porque streaming” sem controle de reconexão vira leak e custo.

Solução

SSE para unidirecional servidor→cliente atrás de HTTP/2 com reconexão simples. WebSocket quando há canal bidirecional contínuo (voz, cancelamento fino, múltiplos eventos multiplexados). Sempre definir formato de frame (NDJSON, SSE data:) e limites.

Arquitetura

Cliente: ReadableStream / EventSource
Servidor Node: transform stream do SDK OpenAI → SSE
Infra: proxies com timeout adequado (nginx `proxy_read_timeout`)

Código

// handler SSE simplificado
export async function GET(req: Request) {
  const { signal } = req;
  const stream = new ReadableStream({
    async start(controller) {
      for await (const token of llmTokens({ signal })) {
        controller.enqueue(encoder.encode(`data: ${token}\n\n`));
      }
      controller.close();
    },
  });
  return new Response(stream, {
    headers: {
      "Content-Type": "text/event-stream",
      Connection: "keep-alive",
      "Cache-Control": "no-cache",
    },
  });
}

Performance

Evitar stringify gigante por chunk; comprimir apenas se intermediário suportar; pooling de conexões no servidor outbound.

Melhorias futuras

Resumable streams; métricas p95 por provedor; circuit breaker se upstream falhar.

Conclusão

Streaming bem feito é UX + rede + cancelamento. Poucos artigos ligam os três — use isso como diferencial.