WebSocket en TypeScript : Construire des applications temps réel avancées
\n\nLes applications temps réel sont devenues une partie essentielle du web moderne. Des chats et notifications aux jeux en ligne et plateformes de trading, la technologie WebSocket assure une connexion bidirectionnelle permanente entre le client et le serveur. À un niveau avancé, le développement de tels systèmes nécessite non seulement une compréhension du protocole, mais aussi la capacité de construire des architectures tolérantes aux pannes et évolutives. Dans cet article, nous verrons comment créer des applications WebSocket de qualité production en TypeScript, en utilisant les meilleures pratiques et modèles.
\n\n1. Architecture et clustering du serveur WebSocket
\n\nUn seul serveur WebSocket ne supportera pas la charge d'un projet réel. La tâche clé est d'organiser le clustering et l'échange de messages entre les instances. Considérons une architecture utilisant la bibliothèque ws et un bus d'événements Redis.
Installons les dépendances :
\nnpm install ws redis ioredis @types/ws @types/ioredis\n\nCréons un serveur clusterisé de base :
\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 pour la communication inter-serveurs\nconst CHANNEL = 'ws:messages';\n\nredisSub.subscribe(CHANNEL, (err, count) => {\n if (err) console.error('Erreur d\'abonnement :', err);\n console.log(`Abonné à ${count} canaux`);\n});\n\n// Réception des messages depuis d'autres instances\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('Nouvelle connexion');\n\n ws.on('message', (data) => {\n const message = data.toString();\n // Publication dans Redis pour toutes les instances\n redisPub.publish(CHANNEL, JSON.stringify({ data: message }));\n });\n});\n\nAvantages :
\n- \n
- Mise à l'échelle horizontale — ajoutez autant de serveurs que vous voulez \n
- Tolérance aux pannes — si une instance tombe, les autres continuent de fonctionner \n
- Bus de messages unifié via Redis Pub/Sub \n
2. Authentification et gestion des sessions
\n\nDans les applications de production, on ne peut pas faire confiance aux connexions non authentifiées. Implémentons la vérification des tokens via JWT lors de l'établissement de la connexion.
\n\nInstallons jsonwebtoken :
npm install jsonwebtoken @types/jsonwebtoken\n\nCréons un middleware pour l'authentification 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 non fourni');\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(`Utilisateur ${decoded.userId} authentifié`);\n return true;\n } catch (err) {\n ws.close(4002, 'Token invalide');\n return false;\n }\n}\n\n// Utilisation\nwss.on('connection', (ws: AuthenticatedSocket, req) => {\n if (!authenticate(ws, req)) return;\n\n ws.on('message', (data) => {\n // Maintenant nous connaissons userId\n console.log(`Message de ${ws.userId} : ${data}`);\n });\n});\n\nRecommandations :
\n- \n
- Transmettez le token via un paramètre de requête ou des en-têtes lors de l'établissement de la connexion \n
- Utilisez des access-tokens à courte durée de vie et des refresh-tokens \n
- Stockez les sessions dans Redis