Cómo implementar una conexión HTTP de streaming en tiempo real para enviar datos desde el servidor al cliente a medida que se van generando.
Para enviar datos de forma continua desde el servidor al navegador usar WebSockets es quizá lo primero que le viene a uno a la mente, pero hay una alternativa menos compleja y sencilla de implementar. En el protocolo HTTP estándar existe una forrma para establecer esta comunicación enviando y recibiendo datos poco a poco sin cerrar la conexión.
Vamos a ver en qué consiste exactamente el streaming en HTTP, cuándo tiene sentido usarlo y cómo implementarlo en un flujo cliente-servidor con JavaScript y Node.js.
Cuando hablamos de streaming HTTP nos referimos a la posibilidad de que el servidor envíe datos al cliente en múltiples fragmentos (o chunks), manteniendo la conexión abierta, en lugar de enviar todo el contenido de una vez y cerrarla. Esto resulta útil para interfaces que deben reaccionar en tiempo real a cambios en el estado de la aplicación (por ejemplo, actualizaciones de sensores, mensajes en directo, logs, etc.).
Aunque no permite comunicación bidireccional como WebSockets, su simplicidad lo hace adecuado para muchas situaciones donde sólo necesitamos que el servidor envíe datos al cliente.
Vamos a partir de este ejemplo de código en el cliente en el que se abre una conexión a una ruta /stream
para iniciar la transferencia de datos:
const response = await fetch("/stream");const reader = response.body.getReader();const decoder = new TextDecoder();
while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); updateState(chunk);}
Este patrón permite leer la respuesta HTTP en tiempo real. fetch
obtiene el cuerpo de la respuesta como ReadableStream
, y con getReader()
accedemos a los fragmentos según vayan llegando. El bucle espera cada nuevo bloque de datos antes de seguir, y updateState()
puede actualizar el DOM, un gráfico o cualquier otra parte de la aplicación con la nueva información.
Veamos ahora cómo podemos construir el servidor para que este cliente funcione correctamente. Usaremos Node.js con Express, que permite controlar las cabeceras y enviar datos manualmente.
const express = require("express");const app = express();
app.get("/stream", (req, res) => { res.setHeader("Content-Type", "text/plain; charset=utf-8"); res.setHeader("Transfer-Encoding", "chunked"); res.setHeader("Cache-Control", "no-cache");
const interval = setInterval(() => { const data = `Estado actualizado: ${new Date().toISOString()}\n`; res.write(data); }, 2000); // enviamos cada 2 segundos
req.on("close", () => { clearInterval(interval); res.end(); });});
app.listen(3000, () => { console.log("Servidor escuchando en http://localhost:3000");});
Con res.write()
enviamos cada fragmento. La conexión permanece abierta hasta que el cliente se desconecta o hasta que decidamos cerrarla manualmente. Es importante configurar correctamente las cabeceras:
Transfer-Encoding: chunked
permite que el servidor envíe datos en bloques sucesivos sin conocer el tamaño total de antemano.Cache-Control: no-cache
evita que el navegador o proxies intermedios almacenen la respuesta.Content-Type
se puede ajustar a application/json
o cualquier otro formato según el tipo de datos.Este patrón es compatible con cualquier navegador moderno que soporte ReadableStream
, y no requiere librerías externas ni protocolos adicionales.
Podemos recurrir al streaming HTTP cuando:
Algunos ejemplos típicos:
El streaming sobre HTTP es una técnica sencilla y útil para enviar datos en tiempo real desde el servidor al cliente sin necesidad de integrar WebSockets ni otras soluciones más complejas. Su implementación requiere entender cómo enviar bloques desde el backend y cómo leerlos progresivamente en el cliente.