Implementando la comunicación WebSocket en Next.js

Nota del editor: Este artículo fue actualizado por última vez por Oyinkansola Awosan el 16 de abril de 2024 para ofrecer una comparación entre WebSocket y Socket.io, cubrir cómo configurar un servidor WebSocket local y usarlo en Next.js, así como probar sus conexiones localmente.
La comunicación en tiempo real en la web utiliza la API WebSocket, un canal de comunicación full-duplex sobre una única conexión TCP. Esto permite que varios clientes se conecten a un servidor y compartan datos en tiempo real, lo que facilita experiencias como juegos multijugador, documentos interactivos, salas de chat y más.
El contenedor WebSocket más famoso para Node.js es Socket.io, un paquete con un servidor Node.js y una biblioteca cliente que se utiliza en el navegador. En este artículo, usaré Socket.io para demostrar una conexión WebSocket entre un servidor y una aplicación Next.js.
Es importante tener en cuenta que las funciones sin servidor en Vercel no son compatibles con WebSockets. Aunque Vercel no es la única opción para implementar una aplicación Next.js, esta limitación podría ser un factor decisivo para algunos. Vercel recomienda usar soluciones de terceros para la comunicación en tiempo real durante las implementaciones en su plataforma. También proporciona una guía para integrar una de estas soluciones de terceros, denominada Pusher.
Pensamiento de diseño empresarial: el marco para un buen diseño de productoIntroducción a WebSockets
Comenzaremos creando una aplicación Next.js con el paquete Create Next App. Ejecutar , donde es el nombre del proyecto. Esto generará una aplicación repetitiva.npx create-next-app websocket-example
websocket-example
Creando el lado del servidor
Al acceder a la carpeta del proyecto, verá la carpeta. Aquí es donde creamos los endpoints que el cliente consumirá. Cualquier archivo que se encuentre debajo se considera un punto final asignado a .pages/api
pages/api
/api/*
Cree un archivo en [nombre del archivo] . Este archivo gestionará la conexión WebSocket, garantizando que solo existe una instancia del WebSocket, a la que accederemos mediante una solicitud en el cliente.socket.js
pages/api
/api/socket
Por defecto, Next.js tiene su propio servidor y no se requiere ninguna configuración a menos que se requiera un servidor personalizado. Para este ejemplo básico de WebSocket, no necesitamos configurar un servidor personalizado. En el archivo, debemos comprobar si ya hay una conexión de socket abierta en el servidor HTTP. De no ser así, conectamos Socket.io.socket.js
Como ya sabrás, para que una ruta API funcione, Next.js requiere que se exporte una función como predeterminada, que tenga los parámetros req
y correspondientes ay, respectivamente.res
http.IncomingMessage
http.ServerResponse
A continuación se muestra un ejemplo de una ruta API:
exportar controlador de función predeterminado ( req , res ) {}
Ahora necesitamos usar este patrón e inicializar una conexión de socket donde no existe ninguna. Para conectar el servidor HTTP a Socket.io, usamos la Server
clase del paquete.Socket.IO
Dentro de la función exportada predeterminada, verificamos que no existe por defecto y muestra que la conexión de socket no está establecida. Si el objeto no existe, iniciamos una nueva conexión de socket instanciando a, que toma como entrada.res.socket.server.io
io
res.socket.server
Server
res.socket.server
A continuación, configuramos el objeto devuelto para que cuando haya una nueva solicitud después de la instanciación, no sea .res.socket.server.io
res.socket.server.io
undefined
Fuera de la if
declaración, simplemente finalizamos la solicitud para evitar que resulte en una solicitud estancada:
importar { Servidor } desde 'Socket.IO' const SocketHandler = ( req , res ) = { if ( res . socket . server . io ) { console . log('El socket ya se está ejecutando') } else { console. log ('El socket se está inicializando') const io = new Server (res. socket. server) res. enchufe. servidor. io = io } res . end () } exportar SocketHandler predeterminado
Usando el punto final en el lado del cliente
Hasta ahora, hemos creado un punto final para inicializar la conexión WebSocket. Ahora necesitamos usarlo en el lado del cliente.
En el directorio, encontrará el archivo que representa la ruta al directorio principal. Dentro de este archivo, implementaremos un componente con un campo de entrada. Luego, enviaremos los cambios en el campo de entrada al servidor para que se transmitan a todos los demás clientes conectados. Este es un ejemplo básico para mostrar la comunicación en tiempo real./pages
index.js
Lo primero que debemos hacer en el componente es establecer la conexión del socket. Para ello, usaremos el useEffect
Hook con una dependencia de matriz vacía, que es el equivalente del componentDidMount
Hook de ciclo de vida.
SocketInitializer
Es una función asíncrona porque necesitamos realizar una llamada al endpoint para abrir la conexión del socket. Una vez resultante esta llamada, podemos inicializar el objeto y asignarle una variable fuera del ámbito de la función para que permanezca invariable durante todo el ciclo de vida del componente./api/socket
io()
Luego, solemos escuchar una emisión y registrar un mensaje para asegurarnos de que el cliente esté conectado. Este es un evento reservado y no debe emitirse manualmente. Puedes encontrar la lista de eventos reservados aquí.socket.on()
connect
Ahora, necesitamos llamar a esta función dentro del useEffect
Hook con una []
dependencia. La razón por la que la envolvemos socketInitializer
dentro de otra función es que las devoluciones de llamada de efectos deben ser sincrónicas para evitar condiciones de carrera:
importar { useEffect } de 'react' importar io de 'Socket.IO-client' dejar socket constante Inicio = () = { useEffect (() = socketInitializer (), []) const socketInitializer = async () = { esperar fetch ( '/api/socket' ) socket = io () socket . on ( 'conectar' , () = { console . log ( 'conectado' ) }) } devolver nulo } exportar predeterminado Inicio ;
Ahora, si visita el puerto en su host local, donde se ejecuta el servidor de desarrollo, verá el mensaje “Conectado” registrado en la consola. Si revisa la pantalla del terminal desde la que inició el servidor de desarrollo, también verá el mensaje “Socket se está inicializando” registrado después de que se conecte el primer cliente. Y si conecta otro cliente visitando el servidor de desarrollo en otra pestaña o pantalla del navegador, verá el mensaje “Socket ya se está ejecutando”.
Emisión de mensajes con Socket.io
Ahora que tenemos la conexión de socket en su lugar, podemos emitir mensajes entre los clientes y el servidor.
Primero, crearemos un campo de entrada controlado. Solo necesitamos un estado y una función controladora para el onChange
evento del campo de entrada:
const [ entrada , setInput ] = useState ( '' ) const onChangeHandler = ( e ) = { setInput ( e . target . value ) socket . emit ( 'cambio de entrada' , e . target . value ) } devolver (entrada marcador de posición = "Escribe algo" valor ={ entrada } onChange ={ onChangeHandler } / )
Dentro de [nombre del onChangeHandler
evento], establecemos el input
estado con un nuevo valor y luego enviamos este cambio al servidor como un evento. El primer parámetro es el nombre único del evento, que es [nombre del evento], y el segundo parámetro es el mensaje. En nuestro caso, este es el valor del campo de entrada.socket.emit()
input-change
A continuación, necesitamos gestionar este evento en el servidor escuchando el evento específico. Al registrar un receptor, nos aseguramos de que se establezca la conexión del socket. Después, dentro de la función de devolución de llamada, nos suscribimos al evento mediante la función, que toma como parámetros el nombre del evento y una función de devolución de llamada:input-change
connection
input-change
socket.on()
yo. on ( 'conexión' , socket = { socket . on ( 'cambio-de-entrada' , msg = { socket . broadcast . emit ( 'actualizar-entrada' , msg ) }) })
Luego, dentro de la función de devolución de llamada, usamos , que envía un mensaje a todos los clientes excepto al que emitió el evento. Por lo tanto, solo los demás clientes reciben, que es el valor de entrada de texto enviado por uno de ellos.socket.broadcast.emit()
input-change
msg
La versión final del archivo es la siguiente:socket.js
importar { Servidor } desde 'Socket.IO' const SocketHandler = ( req , res ) = { if ( res . socket . server . io ) { console . log('El socket ya se está ejecutando') } else { console. log ('El socket se está inicializando') const io = new Server (res. socket. server) res. enchufe. servidor. io = io io . on ( 'conexión' , socket = { socket . on ( 'cambio-de-entrada' , msg = { socket . broadcast . emit ( 'actualizar-entrada' , msg ) }) }) } res . end () } exportar SocketHandler predeterminado
Ahora, volvemos al cliente. Esta vez, necesitamos gestionar el evento transmitido, que fue . Solo necesitamos suscribirnos a este evento con y llamar a la función dentro de la devolución de llamada para actualizar el valor de entrada con el valor enviado desde el servidor:update-input
socket.on()
setInput
importar { useEffect, useState} de 'react' importar io de 'Socket.IO-client' dejar socket; const Home = () = { const [ entrada , setInput ] = useState ( '' ) useEffect (() = socketInitializer (), []) const socketInitializer = async () = { await fetch ( '/api/socket'); socket = io () socket.on ( 'conectar', () = { console.log ( 'conectado') }) socket.on ( 'update-input', msg = { setInput ( msg ) }) } const onChangeHandler = ( e ) = { setInput ( e .target .value) socket. emit ( 'cambio de entrada' , e . target . value ) } devolver ( entrada marcador de posición = "Escribe algo" valor ={ entrada } onChange ={ onChangeHandler } / ) } exportar predeterminado Inicio ;
A continuación se muestra una demostración de dos clientes que se conectan al socket y se actualizan en tiempo real siguiendo los datos enviados al servidor:
Configuración de un servidor WebSocket local
Para integrar la comunicación en tiempo real en una aplicación Next.js, puede configurar un servidor WebSocket local mediante Socket.io. Esto implica crear una configuración de servidor personalizada que Next.js ejecutará, lo que permitirá las conexiones WebSocket junto con las solicitudes HTTP habituales.
Primero, instala Socket.io y agregalo a tu proyecto si aún no lo has hecho:
npm instala socket .io
Next.js suele gestionar las tareas del servidor internamente, pero para la compatibilidad con WebSockets, deberá configurar un servidor personalizado. Cree un archivo en la raíz de su proyecto con el siguiente contenido:server.js
const { createServer } = require('http'); const { parse } = require('url'); const next = require('next'); const { Server } = require("socket.io"); const app = next({ dev : proceso . env . NODE_ENV !== 'producción' }); const handle = app . getRequestHandler(); aplicación.preparar(). then ( () = { const servidor = createServer ( ( req , res ) = { const parsedUrl = parse ( req.url , true ); handle ( req , res , parsedUrl ); } ); const io = new Servidor ( servidor ); io . on ( 'conexión' , socket = { console . log ( 'Cliente conectado' ); socket . on ( 'desconectar' , () = { console log ( 'Cliente desconectado' ); } }); escucha ( 3000 , ( err ) = { if ( err ) throw err ;
Este script configura un servidor Node.js usando HTTP y Socket.io y lo integra con la aplicación Next.js.
Usando el servidor WebSocket en Next.js
Ahora que tiene su servidor WebSocket en funcionamiento, puede modificar su aplicación Next.js para usar este servidor:
- Configuración de la API de ruta: Asegúrese de tener una ruta API en el directorio que inicializa la conexión de Socket.io, si aún no existe. El contenido del archivo es similar al que mencionamos anteriormente.
pages/api
socket.js
- Conexión del lado del cliente: En tus páginas de Next.js, puedes conectarte al servidor WebSocket mediante el cliente Socket.io. Así es como puedes configurarlo:
pages/index.js
importar { useEffect } de 'react' ; importar io de 'socket.io-client' ; export default function Home() { useEffect(() = { const socket = io(); socket.on('connect', () = { console.log('Conectado al servidor WebSocket'); }); return () = { socket.disconnect(); }; }, []); return divWebSocket Test Page/div;}
Probar conexiones WebSocket localmente
Para probar su implementación de WebSocket, inicie el servidor ejecutando node server.js
. Esto hará que su aplicación Next.js esté disponible en http://localhost:3000 con compatibilidad con WebSocket.
A continuación, abra varias pestañas o ventanas del navegador y navegue a http://localhost:3000. Si su conexión WebSocket está configurada correctamente, las acciones en una pestaña (como enviar mensajes) deberían reflejarse en tiempo real en las demás. Utilice herramientas de prueba de WebSocket como Postman o WebSocket King para verificar la capacidad de su servidor en tiempo real.
WebSocket frente a Socket.io
Si bien WebSocket y Socket.io tienen similitudes, difieren en sus funcionalidades y características, y cada uno tiene casos de uso para los que es más adecuado.
WebSocket | Socket.io |
---|---|
WebSocket es un protocolo real | Socket.io es una biblioteca diseñada para trabajar con el protocolo WebSocket |
No se proporciona ninguna opción de respaldo si WebSocket no está disponible | Proporciona una opción de respaldo incorporada si WebSocket no está disponible |
WebSocket es bastante básico y no tiene muchas funciones. | Socket.IO proporciona funciones adicionales como reconexión automática, reconocimiento de mensajes, multiplexación (utilizando diferentes canales) y transmisión a múltiples sockets. |
Fiabilidad
Debido a características como reconexión automática, opción de respaldo, multiplexación, etc., Socket.io puede ser una mejor opción para proyectos o desarrolladores que optimizan la confiabilidad.
Estado latente
Socket.io se basa en WebSocket, lo que significa que cuenta con una capa de abstracción que complementa las demás características de Socket.io. WebSocket tiene la ventaja en este caso, ya que es ideal para aplicaciones que requieren comunicación directa, de baja latencia y dúplex completo con mínima sobrecarga, mientras que Socket.io introduce una latencia ligeramente mayor.
Actuación
Socket.io está diseñado para un proceso de desarrollo más rápido y sencillo, de ahí sus funciones de fácil acceso que lo convierten en uno de los favoritos de los desarrolladores. Sin embargo, ocasionalmente surgen problemas de escalabilidad, especialmente al trabajar con aplicaciones con alta concurrencia de usuarios.
Por otro lado, WebSocket ofrece conexión directa, una sobrecarga mínima y un menor consumo de recursos, lo que puede convertirlo en una mejor opción para los desarrolladores que están optimizando para una baja latencia.
Conclusión
En este artículo, aprendimos cómo integrar Socket.io en Next.js para iniciar una conexión WebSocket para compartir información en tiempo real entre el cliente y el servidor.
También creamos un punto final en el backend de Next.js, que el cliente solicitó para inicializar la conexión de socket. Posteriormente, la comunicación restante entre el servidor y el cliente se gestionó a través de la conexión de socket.
Depurar aplicaciones de Next puede ser difícil, especialmente cuando los usuarios experimentan problemas difíciles de reproducir. Si te interesa supervisar y rastrear el estado, detectar automáticamente errores de JavaScript y rastrear solicitudes de red lentas y el tiempo de carga de los componentes, prueba LogRocket.
El paquete de middleware LogRocket Redux añade una capa adicional de visibilidad a las sesiones de usuario. LogRocket registra todas las acciones y el estado de los almacenes Redux.
Modernice su forma de depurar sus aplicaciones Next.js: comience a monitorearlas de forma gratuita.
Si quieres conocer otros artículos parecidos a Implementando la comunicación WebSocket en Next.js puedes visitar la categoría Guias.
Entradas Relacionadas