WebSocket in JavaScript: Advanced Real-Time Applications

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Beyond Basic WebSocket

A basic understanding of WebSocket allows you to create a simple chat or notifications. However, to build production-grade real-time applications — multiplayer games, financial terminals, collaborative editing systems — you need to master advanced techniques. In this article, we will go beyond "Hello, World" and explore how to create reliable, scalable, and secure WebSocket applications in Node.js.

We will cover four key areas: scaling with Redis, state and room management, security and authorization, and clustering for multi-processor operation. All code examples are written in JavaScript using the ws library and Node.js.



1. Scaling with Pub/Sub (Redis)

One of the main limitations of a WebSocket server is that it cannot "communicate" with other instances. If you have two servers running behind a load balancer, a client connected to server A will not receive a message sent via server B. The solution is to use the Pub/Sub pattern based on Redis.

Redis acts as a central broker: each server subscribes to a channel and publishes messages to it. Thus, a message sent by any server is instantly distributed to all others.

const WebSocket = require('ws');const redis = require('redis');

const wss = new WebSocket.Server({ port: 8080 });const publisher = redis.createClient();const subscriber = redis.createClient();

// Subscribe to the 'broadcast' channelsubscriber.subscribe('broadcast');subscriber.on('message', (channel, message) => { // Broadcast the message to all connected clients wss.clients.forEach(client => { if (client.readyState === WebSocket.OPEN) { client.send(message); } });});

wss.on('connection', (ws) => { ws.on('message', (data) => { // Publish the message to Redis publisher.publish('broadcast', data.toString()); });});

This approach allows you to scale your application horizontally almost indefinitely. When a new server is added, it simply creates its own subscription to Redis.



2. Managing Rooms and States

In most real-time applications, it is necessary to group clients into "rooms" (chat rooms, game lobbies) and manage their state. The basic wss.clients does not provide this functionality. Let's implement a simple but effective room system.

We will create a rooms object, where the key is the room name and the value is a Set of WebSocket clients. We will add methods for joining, leaving, and broadcasting within a room.

const rooms = new Map();

function joinRoom(ws, roomName) { if (!rooms.has(roomName)) { rooms.set(roomName, new Set()); } rooms.get(roomName).add(ws); // Save the room on the ws object for quick access ws.room = roomName;}

function leaveRoom(ws) { if (ws.room && rooms.has(ws.room)) { rooms.get(ws.room).delete(ws); if (rooms.get(ws.room).size === 0) { rooms.delete(ws.room); } delete ws.room; }}

function broadcastToRoom(roomName, message, exclude = null) { if (!rooms.has(roomName)) return; rooms.get(roomName).forEach(client => { if (client !== exclude && client.readyState === WebSocket.OPEN) { client.send(message); } });}

const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', (ws) => { ws.on('message', (data) => { const msg = JSON.parse(data); switch (msg.type) { case 'join': joinRoom(ws, msg.room); break; case 'message': broadcastToRoom(ws.room, msg.text, ws); break; } }); ws.on('close', () => leaveRoom(ws));});

To manage state (e.g., player position, cursor), you can store data in a Map

Blogs

Book Recommendations