WebSocket em TypeScript: Construindo aplicações avançadas em tempo real
\n\nAs aplicações em tempo real tornaram-se parte essencial da web moderna. De chats e notificações a jogos online e plataformas de negociação — a tecnologia WebSocket fornece uma conexão bidirecional constante entre cliente e servidor. Em um nível avançado, o desenvolvimento de tais sistemas requer não apenas a compreensão do protocolo, mas também a capacidade de construir arquiteturas tolerantes a falhas e escaláveis. Neste artigo, vamos analisar como criar aplicações WebSocket de nível de produção em TypeScript, usando as melhores práticas e padrões.
\n\n1. Arquitetura e clusterização do servidor WebSocket
\n\nUm único servidor WebSocket não suportará a carga de um projeto real. A tarefa principal é organizar a clusterização e a troca de mensagens entre as instâncias. Vamos considerar a arquitetura usando a biblioteca ws e o barramento de eventos Redis.
Instalaremos as dependências:
\nnpm install ws redis ioredis @types/ws @types/ioredis\n\nCriaremos um servidor clusterizado básico:
\n\nimport { 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 comunicação entre servidores\nconst CHANNEL = 'ws:messages';\n\nredisSub.subscribe(CHANNEL, (err, count) => {\n if (err) console.error('Erro de inscrição:', err);\n console.log(`Inscrito em ${count} canais`);\n});\n\n// Recebendo mensagens de outras instâncias\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('Nova conexão');\n\n ws.on('message', (data) => {\n const message = data.toString();\n // Publica no Redis para todas as instâncias\n redisPub.publish(CHANNEL, JSON.stringify({ data: message }));\n });\n});\n\nVantagens:
\n- \n
- Escalabilidade horizontal — adicione quantos servidores quiser \n
- Tolerância a falhas — se uma instância cair, as outras continuam funcionando \n
- Barramento de mensagens unificado via Redis Pub/Sub \n
2. Autenticação e gerenciamento de sessões
\n\nEm aplicações de produção, não se pode confiar em conexões não autenticadas. Implementaremos a verificação de tokens via JWT ao estabelecer a conexão.
\n\nInstalaremos jsonwebtoken:
npm install jsonwebtoken @types/jsonwebtoken\n\nCriaremos um middleware para autenticação WebSocket:
\n\nimport { 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 não fornecido');\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(`Usuário ${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 // Agora sabemos o userId\n console.log(`Mensagem de ${ws.userId}: ${data}`);\n });\n});\n\nRecomendações:
\n- \n
- Envie o token via parâmetro de consulta ou cabeçalhos ao estabelecer a conexão \n
- Use tokens de acesso de curta duração e tokens de atualização \n
- Armazene sessões no Redis