Eventos Enviados por el Servidor
Envía actualizaciones del servidor al cliente a través de una conexión HTTP persistente con Eventos Enviados por el Servidor (SSE).
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import express from "express";
const app = express();
app.get("/events", (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 1000);
req.on("close", () => clearInterval(interval));
});Cuándo usarlo: Feeds en vivo, notificaciones, actualizaciones de progreso y transmisión de registros donde solo el servidor envía datos.
Ejemplo de Funcionamiento
import express from "express";
const app = express();
const clients = new Set<import("node:http").ServerResponse>();
app.get("/events", (req, res) => {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"X-Accel-Buffering": "no", // deshabilitar el almacenamiento en búfer de nginx
});
res.write(": connected\n\n"); // la línea de comentario mantiene la conexión activa
clients.add(res);
req.on("close", () => {
clients.delete(res);
});
});
function broadcast(event: string, data: unknown) {
const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
for (const client of clients) {
client.write(message);
}
}
// Activar desde otra parte de la aplicación
app.post("/notify", express.json(), (req, res) => {
broadcast("notification", req.body);
res.json({ sent: clients.size });
});Cliente (navegador):
const source = new EventSource("/events");
source.addEventListener("notification", (e) => {
console.log(JSON.parse(e.data));
});
source.onerror = () => console.log("SSE reconectando...");Lo que esto demuestra:
- Encabezados de respuesta SSE, incluyendo la deshabilitación del búfer de nginx
- Eventos con nombre con el campo
event: - Conjunto de clientes para distribución de difusión
- Limpieza al desconectarse el cliente
EventSourcedel navegador con reconexión automática
Análisis Profundo
Cómo Funciona
- SSE es una respuesta HTTP de larga duración con
Content-Type: text/event-stream - Los mensajes son líneas de texto:
data: ...\n\n(doble salto de línea termina el mensaje) - Campos opcionales:
event:,id:,retry: EventSourcedel navegador se reconecta automáticamente conLast-Event-ID
Formato de Mensaje SSE
event: price-update
id: 42
data: {"symbol": "AAPL", "price": 182.50}
Configuración de nginx
location /events {
proxy_pass http://node_backend;
proxy_buffering off;
proxy_cache off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
}Errores comunes
- nginx almacenando en búfer SSE - los clientes ven eventos retrasados o agrupados. Solución:
proxy_buffering offyX-Accel-Buffering: no. - No limpiar al desconectarse - fuga de memoria por objetos de respuesta obsoletos. Solución: eliminar del conjunto de clientes al
close. - Escribir en una respuesta cerrada - lanza
ERR_STREAM_DESTROYED. Solución: verificarres.writableEndedantes de escribir. - No hay autenticación en el endpoint SSE - cualquiera puede suscribirse. Solución: validar la cookie/token en el GET inicial (EventSource admite cookies).
- Transmitir a todos los clientes sin límite - picos de CPU. Solución: limitar la tasa de difusión; usar salas/canales.
- Usar SSE para bidireccional - el cliente no puede enviar a través de SSE. Solución: el cliente envía a través de HTTP POST normal; el servidor envía a través de SSE.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| WebSocket | Mensajería bidireccional | Envío solo del servidor |
| Long-polling | Restricciones de proxy muy antiguas | Infraestructura moderna |
| Socket.IO | Necesitas salas y fallback | Envío unidireccional simple |
| Webhook al cliente | No aplicable (no hay envío del servidor) | Actualizaciones de UI en tiempo real |
Preguntas Frecuentes
¿SSE o WebSocket para paneles en vivo?
SSE. Los paneles son del servidor al cliente. SSE es más simple, compatible con HTTP y se reconecta automáticamente.
¿Cuántos clientes SSE por proceso de Node?
Similar a WebSocket: 10k-50k conexiones inactivas. La difusión activa reduce la capacidad.
¿Funciona SSE a través de Cloudflare?
Sí, pero verifica los límites de tiempo de espera. Cloudflare puede cerrar conexiones de larga duración después de 100 segundos en el plan gratuito.
¿Cómo autentico SSE?
La autenticación basada en cookies funciona con EventSource. Para la autenticación por token, pásalo como parámetro de consulta (menos seguro) o usa un polyfill de SSE basado en fetch.
¿Puedo usar SSE con Fastify?
Sí, a través de reply.raw para escritura directa de flujo. O usa el plugin @fastify/sse.
¿Qué es el campo retry?
retry: 5000\n\n le dice al navegador que espere 5 segundos antes de reconectarse después de la desconexión.
¿Cómo escalo SSE entre instancias?
Redis pub/sub: cada instancia se suscribe y escribe en sus clientes SSE locales. Consulta Escalado en Tiempo Real.
¿HTTP/2 afecta a SSE?
SSE funciona sobre HTTP/2. La multiplexación ayuda cuando la misma conexión sirve otras solicitudes.
Relacionado
- Conceptos Básicos en Tiempo Real - SSE vs WebSocket
- Escalado en Tiempo Real - SSE multi-instancia
- Consideraciones de HTTP/2 y HTTP/3 - configuración de proxy
- Keep-Alive y Límites de Conexión - ajuste de conexión
- Mejores Prácticas en Tiempo Real - lista de verificación de la sección
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS Activo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.