eduardweb.
AI & LLMsIntermediar#ai#nextjs#react#vercel#sse

Streaming responses de la LLM în Next.js: De la latență de 4 secunde la SSE curat

De Ștefan Iliescu, 30 iul. 2026 · 10 vizualizări · 3 like-uri

Postat 30 iul. 2026
typescript
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';

// Obligatoriu pentru stream-uri lungi fără timeout pe serverless
export const runtime = 'edge';

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

  const result = streamText({
    model: openai('gpt-4o-mini'),
    messages,
    system: 'Ești un asistent tehnic senior. Răspunzi scurt, direct și la obiect.',
  });

  return result.toDataStreamResponse();
}

Acum vreo jumătate de an am adăugat un feature de asistent AI într-un SaaS la care lucrez, cu vreo 12k utilizatori activi lunar. Prima versiune trimitea un request clasic REST, aștepta 4-5 secunde până când OpenAI genera tot răspunsul, și abia apoi afișa ceva pe ecran. Rata de abandon pe pagină era uriașă — oamenii credeau că s-a blocat interfața și dădeau refresh.

Trecerea pe streaming a schimbat complet experiența: primul token apare acum în 250-300ms, iar utilizatorul vede răspunsul construindu-se live. Hai să vedem cum faci asta curat în Next.js App Router și unde poți să te lovești cu capul de prag.

De ce Server-Sent Events (SSE) și nu WebSockets?

WebSockets sunt overkill dacă ai doar un flux unidirecțional (de la server la client). WebSockets mențin o conexiune bidirecțională full-duplex, ceea me complică infrastructura de scaling și necesită servere cu stare (stateful).

Cu SSE (Server-Sent Events), clientul deschide o conexiune HTTP standard cu header-ul Accept: text/event-stream, iar serverul împinge bucăți de text (chunks) pe măsură ce tokenii sunt generați de LLM. Dacă folosești serverless (Vercel, AWS Lambda), SSE se potrivește mănușă pentru că e practic un răspuns HTTP deschis în regim de streaming.

În Next.js App Router, dacă vrei să scrii SSE de la zero, trebuie să te joci cu API-ul nativ ReadableStream. Nu e imposibil, dar e boilerplate obositor: trebuie să parsezi manual liniile cu data: {...}, să gestionezi erorile de rețea și să reconstruiești starea mesajelor pe client.

Vercel AI SDK: De la 80 de linii la 10 linii de cod

Aici își scoate banii pachetul ai. Îți oferă abstracții foarte bune pe două niveluri:

  1. Backend (streamText): Se leagă direct la furnizori (OpenAI, Anthropic, Ollama) și returnează un stream compatibil cu formatul așteptat de frontend.
  2. Frontend (useChat): Un React hook care gestionează automat starea mesajelor, auto-scroll-ul, trimiterea istoricului și opțiunea de stop/retry.

Pe server (într-un Route Handler din Next.js), totul se rezumă la apelarea streamText și returnarea răspunsului generat cu metoda .toDataStreamResponse().

Trade-off-uri reale și bube din producție

Nu totul e perfect când treci pe streaming. Am adunat câteva lecții dureroase după câteva luni în producție:

1. Runtime-ul pe Serverless (Node vs Edge) Dacă rulezi pe runtime-ul implicit de Node.js pe serverless, conexiunea SSE ține funcția lambda activă pe toată durata generării (să zicem 8-10 secunde). Pe Vercel Hobby/Pro ai limite de execution timeout. Pentru stream-uri lungi, ești obligat să treci endpoint-ul pe Edge Runtime (export const runtime = 'edge'). Dar atenție: pe Edge pierzi accesul la unele module Node native (cum ar fi fs sau anumite pachete de crypto/database drivers).

2. Re-render-uri agresive pe UI Deoarece useChat actualizează starea mesajului la fiecare token primit (adică o dată la 20-50ms), tot arborele de componente din chat se re-randează extrem de des. Dacă folosești o librărie grea de parsare Markdown (react-markdown cu syntax highlighting pentru cod), CPU-ul clientului o să sară în 100% și scroll-ul va agăța. Trucul este să mezoizezi componentele de mesaj cu React.memo și să randezi Markdown-ul greu doar pentru mesajele finalizate, iar pentru cel în streaming să folosești un renderer simplificat.

3. Costurile ascunse de infrastructură O conexiune HTTP care stă deschisă 10 secunde consumă resurse de gateway. Dacă ai 500 de utilizatori simultani care primesc stream-uri, numărul de conexiuni concurente pe load balancer explodează față de un request clasic de 100ms.

Voi ce folosiți?

Ați mers pe abstracția oficială de la Vercel sau preferați un client custom peste Fetch EventSource? Mă interesează mai ales cum ați rezolvat problema de rate-limiting pe stream-uri când aveți utilizatori anonimi.

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.