WebSocket en TypeScript: desarrollo avanzado de aplicaciones en tiempo real

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

WebSocket en TypeScript: Construyendo aplicaciones avanzadas en tiempo real

\n\n

Las aplicaciones en tiempo real se han convertido en una parte esencial de la web moderna. Desde chats y notificaciones hasta juegos en línea y plataformas de trading, la tecnología WebSocket proporciona una conexión bidireccional permanente entre el cliente y el servidor. A nivel avanzado, el desarrollo de estos sistemas requiere no solo comprender el protocolo, sino también saber construir arquitecturas tolerantes a fallos y escalables. En este artículo, analizaremos cómo crear aplicaciones WebSocket de grado de producción en TypeScript, utilizando las mejores prácticas y patrones.

\n\n

1. Arquitectura y clustering del servidor WebSocket

\n\n

Un único servidor WebSocket no soportará la carga de un proyecto real. La tarea clave es organizar el clustering y el intercambio de mensajes entre instancias. Consideremos la arquitectura utilizando la biblioteca ws y el bus de eventos Redis.

\n\n

Instalemos las dependencias:

\n
npm install ws redis ioredis @types/ws @types/ioredis
\n\n

Creemos un servidor clusterizado básico:

\n\n
import { WebSocketServer, WebSocket } from 'ws';\nimport Redis from 'ioredis';\n\nconst redisPub = new Redis();\nconst redisSub = new Redis();\n\nconst wss = new WebSocketServer({ port: 8080 });\n\n// Canal para comunicación entre servidores\nconst CHANNEL = 'ws:messages';\n\nredisSub.subscribe(CHANNEL, (err, count) => {\n  if (err) console.error('Error de suscripción:', err);\n  console.log(`Suscrito a ${count} canales`);\n});\n\n// Recibir mensajes de otras instancias\nredisSub.on('message', (channel, message) => {\n  const parsed = JSON.parse(message);\n  wss.clients.forEach((client) => {\n    if (client.readyState === WebSocket.OPEN) {\n      client.send(parsed.data);\n    }\n  });\n});\n\nwss.on('connection', (ws) => {\n  console.log('Nueva conexión');\n\n  ws.on('message', (data) => {\n    const message = data.toString();\n    // Publicar en Redis para todas las instancias\n    redisPub.publish(CHANNEL, JSON.stringify({ data: message }));\n  });\n});
\n\n

Ventajas:

\n
    \n
  • Escalabilidad horizontal: agregue tantos servidores como desee
  • \n
  • Tolerancia a fallos: si una instancia falla, las demás continúan funcionando
  • \n
  • Bus de mensajes unificado a través de Redis Pub/Sub
  • \n
\n\n

2. Autenticación y gestión de sesiones

\n\n

En aplicaciones de producción, no se puede confiar en conexiones no autenticadas. Implementemos la verificación de tokens mediante JWT al establecer la conexión.

\n\n

Instalemos jsonwebtoken:

\n
npm install jsonwebtoken @types/jsonwebtoken
\n\n

Creemos un middleware para la autenticación WebSocket:

\n\n
import { IncomingMessage } from 'http';\nimport jwt from 'jsonwebtoken';\n\nconst JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';\n\ninterface AuthenticatedSocket extends WebSocket {\n  userId?: string;\n}\n\nfunction authenticate(ws: AuthenticatedSocket, req: IncomingMessage): boolean {\n  const url = new URL(req.url || '', 'http://localhost');\n  const token = url.searchParams.get('token');\n\n  if (!token) {\n    ws.close(4001, 'Token no proporcionado');\n    return false;\n  }\n\n  try {\n    const decoded = jwt.verify(token, JWT_SECRET) as { userId: string };\n    ws.userId = decoded.userId;\n    console.log(`Usuario ${decoded.userId} autenticado`);\n    return true;\n  } catch (err) {\n    ws.close(4002, 'Token inválido');\n    return false;\n  }\n}\n\n// Uso\nwss.on('connection', (ws: AuthenticatedSocket, req) => {\n  if (!authenticate(ws, req)) return;\n\n  ws.on('message', (data) => {\n    // Ahora conocemos el userId\n    console.log(`Mensaje de ${ws.userId}: ${data}`);\n  });\n});
\n\n

Recomendaciones:

\n
    \n
  • Transmita el token mediante un parámetro de consulta o encabezados al establecer la conexión
  • \n
  • Utilice tokens de acceso de corta duración y tokens de actualización
  • \n
  • Almacene las sesiones en Redis
  • \n

Blogs

Book Recommendations