En el artículo anterior explicamos las diferencias principales entre Server-Sent events y WebSockets. Vamos ahora a ver un ejemplo real de SSE en NodeJS, para entenderlo.
Estructura principal del ejemplo
Recordemos que el aspecto clave de Server-Sent Events es que la comunicación es unidireccional, por lo que sólo podemos comunicarnos de servidor a cliente.
La estructura principal de nuestro ejemplo sería el siguiente:
import { readFile } from "node:fs/promises";
import { createServer } from "node:http";
const PORT = 4321;
const server = createServer(async (req, res) => {
const isClient = req.url === "/";
const isServer = req.url === "/events";
if (isServer) {
// Código del servidor
}
else if (isClient) {
// Código del cliente
}
else {
res.writeHead(404, "Not found");
}
});
server.listen(PORT, () => console.log(`Escuchando en http://localhost:${PORT}`));
Observa que hemos creado un servidor donde escuchamos dos rutas:
- 1️⃣
/events
es la ruta del servidor SSE. - 2️⃣
/
es la ruta del cliente (navegador) - 3️⃣ En el caso de acceder a cualquier otra ruta, obtendremos un
404
error.
Veamos ahora el fragmento de código de cada parte, servidor y cliente.
El servidor
En el caso del servidor vamos a enviar una cabecera Content-Type
con el valor text/event-stream
. Esto nos indica que estamos enviando streams de datos, es decir, un grupo de información cada cierto tiempo:
if (isServer) {
res.setHeader("Content-Type", "text/event-stream");
res.writeHead(200);
const timer = setInterval(() => {
const data = { message: "Hello!", timestamp: new Date().toISOString() }
res.write(`data: ${JSON.stringify(data)}\n\n`);
}), 5000);
req.on("close", () => clearInterval(timer));
}
Un poco más abajo creamos un temporizador setInterval() que cada 5 segundos
enviará el contenido del objeto data
a través del stream de datos del servidor.
El cliente
Ahora vamos con el fragmeto de código del cliente, donde simplemente leemos el contenido del archivo index.html
y lo enviamos al navegador:
if (isClient) {
const textContent = await readFile("./index.html", { encoding: "utf-8" });
res.writeHead(200, textContent);
}
El contenido de dicho index.html
sería el siguiente:
- Creamos un HTML básico, donde mostramos un
<h1>
y un<div>
contenedor vacío. - Un poco más abajo, le damos un estilo CSS básico.
<h1>Start</h1>
<div class="container"></div>
<style>
body {
background: indigo;
color: white;
font-size: 3rem;
}
</style>
<script type="module">
if (EventSource) {
const source = new EventSource('/events');
const container = document.querySelector(".container");
source.addEventListener("message", ({ data }) => {
const { message, timestamp } = JSON.parse(data);
const element = document.createElement("div");
element.textContent = `[${timestamp}] Message: ${message}`;
container.append(element);
});
}
</script>
Vamos ahora al código Javascript. Tras comprobar si está soportado, creamos un nuevo objeto EventSource
con la ruta a la que se conectará para obtener los datos. A continuación, simplemente escuchamos los eventos message
y cada vez que se reciba uno, añadimos un nuevo elemento al DOM de la página HTML.