Cómo utilizar eventos enviados por el servidor en Node.js para crear una aplicación en tiempo real
Introducción
Server-Sent Events (SSE) es una tecnología basada en HTTP. Del lado del cliente, proporciona una API llamada EventSource
(parte del estándar HTML5) que nos permite conectarnos al servidor y recibir actualizaciones de este.
Antes de tomar la decisión de utilizar eventos enviados por el servidor, debemos tener en cuenta dos aspectos muy importantes:
- Solo permite la recepción de datos desde el servidor (unidireccional)
- Los eventos están limitados a UTF-8 (sin datos binarios)
Si su proyecto solo recibe algo como precios de acciones o información de texto sobre algo en progreso, es un candidato para usar eventos enviados por el servidor en lugar de una alternativa como WebSockets .
En este artículo, creará una solución completa para el backend y el frontend para manejar la información en tiempo real que fluye del servidor al cliente. El servidor se encargará de enviar nuevas actualizaciones a todos los clientes conectados y la aplicación web se conectará al servidor, recibirá estas actualizaciones y las mostrará.
Prerrequisitos
Para seguir este tutorial, necesitarás:
- Un entorno de desarrollo local para Node.js. Sigue Cómo instalar Node.js y crear un entorno de desarrollo local .
- Familiaridad con Express.
- Familiaridad con React (y hooks ).
- Se utiliza cURL para verificar los puntos finales. Es posible que ya esté disponible en su entorno o que deba instalarlo. También será útil tener cierta familiaridad con el uso de herramientas y opciones de línea de comandos.
Este tutorial fue verificado con cURL v7.64.1, Node v15.3.0, npm
v7.4.0, express
v4.17.1, body-parser
v1.19.0, cors
v2.8.5 y react
v17.0.1.
Paso 1: creación del backend de SSE Express
En esta sección, creará un nuevo directorio de proyecto. Dentro del directorio de proyecto habrá un subdirectorio para el servidor. Más adelante, también creará un subdirectorio para el cliente.
Primero, abre tu terminal y crea un nuevo directorio de proyecto:
- mkdir node-sse-example
Navegue hasta el directorio del proyecto recién creado:
- cd node-sse-example
A continuación, cree un nuevo directorio de servidor:
- mkdir sse-server
Navegue hasta el directorio del servidor recién creado:
- cd sse-server
Inicializar un nuevo npm
proyecto:
- npm init -y
Instalar express
, body-parser
, y cors
:
- npm install express@4.17.1 body-parser@1.19.0 cors@2.8.5 --save
Esto completa la configuración de dependencias para el backend.
En esta sección, desarrollará el backend de la aplicación. Deberá admitir estas funciones:
- Realizar un seguimiento de las conexiones abiertas y los cambios de transmisión cuando se agregan nuevos datos
GET /events
Punto final para registrarse para actualizacionesPOST /facts
punto final para nuevos hechosGET /status
punto final para saber cuántos clientes se han conectadocors
middleware para permitir conexiones desde la aplicación frontend
Utilice la primera sesión de terminal que se encuentre en el sse-server
directorio. Cree un nuevo server.js
archivo:
Abra el server.js
archivo en su editor de código. Solicite los módulos necesarios e inicialice la aplicación Express:
servidor sse/servidor.js
const express = require('express');const bodyParser = require('body-parser');const cors = require('cors');const app = express();app.use(cors());app.use(bodyParser.json());app.use(bodyParser.urlencoded({extended: false}));app.get('/status', (request, response) = response.json({clients: clients.length}));const PORT = 3000;let clients = [];let facts = [];app.listen(PORT, () = { console.log(`Facts Events service listening at http://localhost:${PORT}`)})
A continuación, cree el middleware para GET
las solicitudes al /events
punto final. Agregue las siguientes líneas de código a server.js
:
servidor sse/servidor.js
// ...function eventsHandler(request, response, next) { const headers = { 'Content-Type': 'text/event-stream', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache' }; response.writeHead(200, headers); const data = `data: ${JSON.stringify(facts)}nn`; response.write(data); const clientId = Date.now(); const newClient = { id: clientId, response }; clients.push(newClient); request.on('close', () = { console.log(`${clientId} Connection closed`); clients = clients.filter(client = client.id !== clientId); });}app.get('/events', eventsHandler);
El eventsHandler
middleware recibe los objetos request
y response
que proporciona Express.
Los encabezados son necesarios para mantener abierta la conexión. El Content-Type
encabezado se establece en 'text/event-stream'
y el Connection
encabezado se establece en 'keep-alive'
. El Cache-Control
encabezado es opcional, se establece en 'no-cache'
. Además, el estado HTTP se establece en 200
: el código de estado para una solicitud exitosa.
Después de que un cliente abre una conexión, los datos facts
se convierten en una cadena. Debido a que se trata de un transporte basado en texto, debe convertir la matriz en cadena ; además, para cumplir con el estándar, el mensaje necesita un formato específico. Este código declara un campo llamado data
y le asigna la matriz convertida en cadena. El último detalle a destacar es que la nueva línea final doble nn
es obligatoria para indicar el final de un evento.
Se genera A clientId
en función de la marca de tiempo y del response
objeto Express. Estos se guardan en la clients
matriz. Cuando A client
cierra una conexión, la matriz de clients
se actualiza a filter
out that client
.
A continuación, cree el middleware para POST
las solicitudes al /fact
punto final. Agregue las siguientes líneas de código a server.js
:
servidor sse/servidor.js
// ...function sendEventsToAll(newFact) { clients.forEach(client = client.response.write(`data: ${JSON.stringify(newFact)}nn`))}async function addFact(request, respsonse, next) { const newFact = request.body; facts.push(newFact); respsonse.json(newFact) return sendEventsToAll(newFact);}app.post('/fact', addFact);
El objetivo principal del servidor es mantener a todos los clientes conectados e informados cuando se agregan nuevos datos. El addNest
middleware guarda el dato, lo devuelve al cliente que realizó POST
la solicitud e invoca la sendEventsToAll
función.
sendEventsToAll
itera la clients
matriz y utiliza el write
método de cada objeto Express response
para enviar la actualización.
Paso 2: Prueba del backend
Antes de implementar la aplicación web, puede probar su servidor usando cURL:
En una ventana de terminal, navegue hasta el sse-server
directorio de su proyecto y ejecute el siguiente comando:
- node server.js
Mostrará el siguiente mensaje:
- OutputFacts Events service listening at http://localhost:3001
En una segunda ventana de terminal, abra una conexión en espera de actualizaciones con el siguiente comando:
- curl -H Accept:text/event-stream http://localhost:3001/events
Esto generará la siguiente respuesta:
- Outputdata: []
Una matriz vacía.
En una tercera ventana de terminal, cree una solicitud POST posterior para agregar un nuevo hecho con el siguiente comando:
- curl -X POST
- -H "Content-Type: application/json"
- -d '{"info": "Shark teeth are embedded in the gums rather than directly affixed to the jaw, and are constantly replaced throughout life.", "source": "https://en.wikipedia.org/wiki/Shark"}'
- -s http://localhost:3001/fact
Después de la POST
solicitud, la segunda ventana del terminal debería actualizarse con el nuevo hecho:
- Outputdata: {"info": "Shark teeth are embedded in the gums rather than directly affixed to the jaw, and are constantly replaced throughout life.", "source": "https://en.wikipedia.org/wiki/Shark"}
Ahora la facts
matriz se rellena con un elemento si cierra la comunicación en la segunda pestaña y la abre nuevamente:
- curl -H Accept:text/event-stream http://localhost:3001/events
En lugar de una matriz vacía, ahora debería recibir un mensaje con este nuevo elemento:
- Outputdata: [{"info": "Shark teeth are embedded in the gums rather than directly affixed to the jaw, and are constantly replaced throughout life.", "source": "https://en.wikipedia.org/wiki/Shark"}]
En este punto, el backend está completamente funcional. Ahora es momento de implementar la EventSource
API en el frontend.
Paso 3: creación de la interfaz de usuario de la aplicación web React
En esta parte de nuestro proyecto, escribirás una aplicación React que use la EventSource
API.
La aplicación web tendrá el siguiente conjunto de características:
- Abrir y mantener una conexión con nuestro servidor previamente desarrollado
- Renderizar una tabla con los datos iniciales
- Mantenga la tabla actualizada a través de SSE
Ahora, abre una nueva ventana de terminal y navega hasta el directorio del proyecto. Úsalo create-react-app
para generar una aplicación React.
- npx create-react-app sse-client
Navegue hasta el directorio de cliente recién creado:
- cd sse-client
Ejecute la aplicación cliente:
- npm start
Esto debería abrir una nueva ventana del navegador con tu nueva aplicación React. Esto completa la configuración de las dependencias para el frontend.
Para darle estilo, abre el App.css
archivo en tu editor de código y modifica el contenido con las siguientes líneas de código:
cliente sse/src/App.css
body { color: #555; font-size: 25px; line-height: 1.5; margin: 0 auto; max-width: 50em; padding: 4em 1em;}.stats-table { border-collapse: collapse; text-align: center; width: 100%;}.stats-table tbody tr:hover { background-color: #f5f5f5;}
A continuación, abre el App.js
archivo en tu editor de código y modifica el contenido con las siguientes líneas de código:
cliente sse/src/App.js
import React, { useState, useEffect } from 'react';import './App.css';function App() { const [ facts, setFacts ] = useState([]); const [ listening, setListening ] = useState(false); useEffect( () = { if (!listening) { const events = new EventSource('http://localhost:3001/events'); events.onmessage = (event) = { const parsedData = JSON.parse(event.data); setFacts((facts) = facts.concat(parsedData)); }; setListening(true); } }, [listening, facts]); return ( table className="stats-table" thead tr thFact/th thSource/th /tr /thead tbody { facts.map((fact, i) = tr key={i} td{fact.info}/td td{fact.source}/td /tr ) } /tbody /table );}export default App;
El useEffect
argumento de la función contiene las partes importantes: un EventSource
objeto con el /events
punto final y un onmessage
método donde data
se analiza la propiedad del evento.
A diferencia de la cURL
respuesta, ahora tienes el evento como un objeto. Ahora puedes tomar la data
propiedad y analizarla, lo que te dará como resultado un objeto JSON válido.
Finalmente, este código inserta el nuevo hecho en la lista de hechos y la tabla se vuelve a representar.
Paso 4 – Prueba del frontend
Ahora, intenta agregar un nuevo hecho.
En una ventana de terminal, ejecute el siguiente comando:
- curl -X POST
- -H "Content-Type: application/json"
- -d '{"info": "Shark teeth are embedded in the gums rather than directly affixed to the jaw, and are constantly replaced throughout life.", "source": "https://en.wikipedia.org/wiki/Shark"}'
- -s http://localhost:3001/fact
La POST
solicitud agregó un nuevo dato y todos los clientes conectados deberían haberlo recibido. Si revisa la aplicación en el navegador, tendrá una nueva fila con esta información.
Conclusión
Este artículo sirvió como introducción a los eventos enviados por el servidor. En este artículo, creó una solución completa para el backend y el frontend para manejar la información en tiempo real que fluye del servidor al cliente.
Los SSE fueron diseñados para el transporte unidireccional y basado en texto. Aquí se muestra el soporte actual para EventSource
los navegadores .
Continúa tu aprendizaje explorando todas las funciones disponibles paraEventSource
dar me gusta retry
.
Deja una respuesta