WebSockets Cheatsheet
Broadcasting
Use this WebSockets reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Broadcast to All Connected Clients
// ws library — wss.clients is a Set<WebSocket> function broadcast(wss, data) { for (const client of wss.clients) { if (client.readyState === WebSocket.OPEN) { client.send(data); } } } wss.on("connection", (ws) => { ws.on("message", (msg) => broadcast(wss, msg)); });
Broadcast to All Except Sender
function broadcastExcept(wss, sender, data) { for (const client of wss.clients) { if (client !== sender && client.readyState === WebSocket.OPEN) { client.send(data); } } } wss.on("connection", (ws) => { ws.on("message", (msg) => broadcastExcept(wss, ws, msg)); });
Typed Broadcast with JSON Envelope
function broadcastJSON(wss, type, payload, exclude = null) { const frame = JSON.stringify({ type, payload, ts: Date.now() }); for (const client of wss.clients) { if (client !== exclude && client.readyState === WebSocket.OPEN) { client.send(frame); } } } // Usage wss.on("connection", (ws) => { broadcastJSON(wss, "join", { count: wss.clients.size }); ws.on("message", (raw) => { const { type, payload } = JSON.parse(raw.toString()); broadcastJSON(wss, type, payload, ws); // exclude sender }); ws.on("close", () => { broadcastJSON(wss, "leave", { count: wss.clients.size }); }); });
Targeted Send (Unicast)
// Tag each socket with a userId on connect wss.on("connection", (ws, req) => { const params = new URL(req.url, "http://x").searchParams; ws.userId = params.get("userId"); }); function sendToUser(wss, userId, data) { for (const client of wss.clients) { if (client.userId === userId && client.readyState === WebSocket.OPEN) { client.send(data); break; } } }
Managing a Custom Client Map
// Map for O(1) user lookups const clients = new Map(); // userId → Set<WebSocket> (multiple tabs) wss.on("connection", (ws, req) => { const userId = authenticate(req); if (!clients.has(userId)) clients.set(userId, new Set()); clients.get(userId).add(ws); ws.on("close", () => { clients.get(userId)?.delete(ws); if (clients.get(userId)?.size === 0) clients.delete(userId); }); }); function sendToUser(userId, data) { const sockets = clients.get(userId); if (!sockets) return; for (const ws of sockets) { if (ws.readyState === WebSocket.OPEN) ws.send(data); } }
Broadcast Patterns Summary
| Pattern | Recipient | Code pattern |
|---|---|---|
| Broadcast all | Every connected client | for (client of wss.clients) |
| Broadcast except sender | Everyone but the sender | client !== ws guard |
| Unicast | One specific user | Look up by ws.userId |
| Multicast / room | A subset | Custom Set or Map per group |
| Tagged broadcast | Clients matching a predicate | Filter in loop |
Efficient Binary Broadcast (avoid re-serializing)
// Serialize once, send the same Buffer to all clients function broadcastBinary(wss, buffer) { const data = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer); for (const client of wss.clients) { if (client.readyState === WebSocket.OPEN) { client.send(data, { binary: true }); } } }
Sending the same
Bufferreference to multiple clients is safe —wsdoes not mutate the buffer.
Presence: Track Online Users
const onlineUsers = new Set(); wss.on("connection", (ws, req) => { const userId = getUserId(req); ws.userId = userId; onlineUsers.add(userId); broadcastJSON(wss, "presence", { online: [...onlineUsers] }); ws.on("close", () => { onlineUsers.delete(userId); broadcastJSON(wss, "presence", { online: [...onlineUsers] }); }); });
Rate-Limiting Broadcast
const sendTimes = new Map(); // ws → last send timestamp function rateLimitedBroadcast(ws, wss, data) { const now = Date.now(); const last = sendTimes.get(ws) ?? 0; if (now - last < 100) return; // max 10 msgs/sec per client sendTimes.set(ws, now); broadcastExcept(wss, ws, data); }
Gotchas
wss.clientsincludes sockets inCLOSINGstate — always checkreadyState === WebSocket.OPEN.wss.clientsis aSet, not an array — usefor...of, not.forEach()index access.- For high-frequency broadcasts (game loops), serialize the message once outside the loop rather than inside.
- Iterating
wss.clientswhile clients connect/disconnect is safe, but not because of anythingwsdoes — it is a plainSet, and JSSetiterators tolerate mutation (deleted entries are skipped; entries added mid-iteration are visited). - For multi-process or multi-server deployments,
wss.clientsonly contains clients connected to this process. Use Redis pub/sub to fan out across nodes (see Scaling page).