ArquitecturaAvanzado35 min
Streaming y SSE en Tiempo Real
Server-Sent Events · chunks · UX fluida · respuestas que aparecen letra a letra
StreamingSSERealtime
✓ Guía gratuita · Acceso completo
Mapa rápido · salta al paso que te interese
01
Fundamentos: SSE vs WebSockets vs Polling
Server-Sent Events es un estándar HTTP que permite al servidor enviar datos al cliente de forma continua sobre una única conexión. Es más simple que WebSockets y perfecto para streaming de IA.
| Método | Dirección | Complejidad | Caso de uso |
|---|---|---|---|
| SSE | Server → Client | Baja | Streaming de texto, notificaciones |
| WebSockets | Bidireccional | Media | Chat en tiempo real, juegos |
| Long Polling | Client → Server | Alta | Fallback legacy |
Tip Pro
SSE funciona sobre HTTP/1.1 y HTTP/2, pasa por proxies sin problemas, y se reconecta automáticamente si la conexión se pierde.
02
Backend: API route con streaming
El truco está en devolver un ReadableStream y configurar los headers correctos. El content-type debe ser text/event-stream.
app/api/chat/route.ts
import Anthropic from '@anthropic-ai/sdk'
const anthropic = new Anthropic()
export async function POST(req: Request) {
const { messages } = await req.json()
const stream = await anthropic.messages.stream({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages,
})
// Crear un ReadableStream desde el stream de Anthropic
const readableStream = new ReadableStream({
async start(controller) {
for await (const event of stream) {
if (event.type === 'content_block_delta' &&
event.delta.type === 'text_delta') {
// Formato SSE: "data: contenido\n\n"
controller.enqueue(
new TextEncoder().encode(`data: ${JSON.stringify({ text: event.delta.text })}\n\n`)
)
}
}
controller.enqueue(new TextEncoder().encode('data: [DONE]\n\n'))
controller.close()
}
})
return new Response(readableStream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
})
}03
Frontend: consumir el stream en React
En el cliente, usamos fetch con un reader para procesar los chunks según llegan.
hooks/useStreamingChat.ts
import { useState, useCallback } from 'react'
export function useStreamingChat() {
const [response, setResponse] = useState('')
const [isLoading, setIsLoading] = useState(false)
const sendMessage = useCallback(async (messages: Array<{role: string, content: string}>) => {
setIsLoading(true)
setResponse('')
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages }),
})
const reader = res.body?.getReader()
const decoder = new TextDecoder()
while (reader) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
const lines = chunk.split('\n')
for (const line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
const data = JSON.parse(line.slice(6))
setResponse(prev => prev + data.text)
}
}
}
setIsLoading(false)
}, [])
return { response, isLoading, sendMessage }
}Tip Pro
Para producción, considera usar el Vercel AI SDK que maneja todo esto con useChat y useCompletion.
¿Quieres el sistema completo?
500+ páginas, 25 capítulos, código de producción, acceso a todas las guías del Arsenal.
Curso Completo · €29PayPal · Acceso inmediato