Server-Sent Events (SSE): Comunicación en tiempo real simplificada

17-12-2025

Por Mario S. Pinto Miranda

En el desarrollo web hay contextos donde la comunicación entre cliente y servidor no puede ser satisfecha con la comunicación por defecto HTTP vía request-response, como en el caso de notificaciones instantáneas, actualizaciones o resolución parcial de la información, los Server-Sent Events (SSE) dan una buena solución frente a otras como los web sockets. En el artículo te cuento cómo funcionan y cuándo utilizarlos. Todo esto surge a consecuencia de estudiarlo para aplicarlo en el contexto de aplicaciones que usan IA permitiendo el streaming del procesamiento de los agentes ante una petición de un usuario.

¿Qué son los SSE?

Los Server-Sent Events (SSE) permiten que el servidor envíe datos de forma continua y unidireccional a un cliente (como un navegador) tras una única conexión inicial. A diferencia de las peticiones HTTP clásicas (en las que el cliente pregunta una y otra vez), con SSE el servidor empuja la información tan pronto como está disponible. Es como una emisión de radio: el servidor transmite, el cliente escucha.


Características principales

  1. Comunicación unidireccional El flujo va del servidor al cliente, no al revés.

  2. Formato de eventos en texto Los mensajes se envían como texto con una sintaxis específica. Cada evento termina con una línea en blanco (\n\n). Ejemplo:

    data: {"stock": "GOOG", "price": 1500}
  3. API nativa en navegadores: EventSource Sin librerías externas. Ejemplo básico:

    const source = new EventSource("/stock-updates"); source.addEventListener("message", (event) => { console.log(event.data); // Contiene el payload del servidor });
  4. Reconexión automática Si la conexión se cae, el navegador reintenta de forma automática. Según la especificación HTML, el tiempo de reconexión es definido por la implementación, típicamente unos pocos segundos. El servidor puede sugerir el intervalo con retry::

    retry: 5000 data: Bienvenido
  5. Identificadores de evento (id) Permiten reanudar sin perder datos. Cuando el cliente se reconecta, envía el header Last-Event-ID con el último id recibido, y el servidor puede continuar desde ahí (especificación HTML):

    id: 123 data: {"stock": "GOOG", "price": 1500} id: 124 data: {"stock": "GOOG", "price": 1502}

Ejemplo con Node.js y Express: Agente IA con patrón ReAct

Servidor (endpoint SSE para agente IA):

app.get("/ai-agent", (req, res) => { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); let aborted = false; req.on("close", () => { aborted = true; }); // Helper para esperar const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // Función para enviar eventos SSE const sendEvent = (res, id, data) => { if (aborted) return; res.write(`id: ${id}\n`); res.write(`data: ${JSON.stringify(data)}\n\n`); }; // Simulación de pasos del agente IA con ReAct (sería un loop while si tuviéramos un agente real) // cada paso envía un evento SSE que el cliente podrá procesar y decidir cómo actuar para renderizar la información (async () => { sendEvent(res, 1, { type: "reasoning", content: "Analizando la consulta del usuario..." }); await sleep(1000); sendEvent(res, 2, { type: "action", content: "Buscando información en la base de datos..." }); await sleep(1000); sendEvent(res, 3, { type: "observation", content: "Encontrados 3 resultados relevantes" }); await sleep(1000); sendEvent(res, 4, { type: "reasoning", content: "Evaluando la mejor respuesta basada en los datos..." }); await sleep(1000); sendEvent(res, 5, { type: "action", content: "Generando respuesta final..." }); await sleep(1000); sendEvent(res, 6, { type: "result", content: "Aquí está la información que solicitaste..." }); await sleep(1000); sendEvent(res, 7, { type: "final", content: "Proceso completado." }); res.end(); })(); });

SSE vs Polling vs WebSockets

  • REST + Polling El cliente pregunta periódicamente (p. ej., cada 5s). Simple pero ineficiente y con mayor latencia.

  • SSE Ideal cuando el cliente solo recibe datos en tiempo real: notificaciones, feeds, marcadores, precios. Más eficiente que el polling y fácil de implementar. MDN recomienda SSE para comunicación unidireccional del servidor al cliente.

  • WebSockets Comunicación bidireccional. Perfecto para chats, juegos multijugador, edición colaborativa. Si el cliente también necesita enviar datos de forma interactiva y constante, WebSockets suele ser la opción.

Nota importante: Según MDN, SSE tiene limitaciones en el número de conexiones concurrentes cuando no se usa HTTP/2 (6 conexiones por dominio en la mayoría de navegadores).


Ventajas de los SSE

  • Implementación sencilla con API nativa (EventSource) disponible en todos los navegadores modernos.
  • Reconexión automática y manejo de eventos integrado según especificación HTML.
  • Menor consumo que el polling (menos peticiones, menor latencia percibida).
  • Funciona sobre HTTP/1.1 y encaja bien con infraestructura existente.
  • Formato de texto simple y comprensible (text/event-stream).

Conclusión

Los Server-Sent Events son una solución excelente cuando el cliente necesita escuchar actualizaciones en tiempo real de forma eficiente y sencilla. Para casos de interacción bidireccional (chats, colaboración o juegos), WebSockets será generalmente la elección adecuada. En mi proyecto actual encaja muy bien SSE para poder abrir una conexión durante una interacción con un agente IA que va reportando sus avances o plan de trabajo a medida que lo ejecuta en un loop hasta dar un resultado final que también dispara el cierre de la comunicación y request.


Referencias y fuentes

  1. HTML Living Standard - Server-sent events - WHATWG (Especificación oficial)
  2. MDN Web Docs - Server-sent events - Mozilla Developer Network
  3. MDN Web Docs - Using server-sent events - Guía práctica de implementación
  4. JavaScript.info - Server Sent Events - Tutorial detallado con ejemplos