WebSockets Cheatsheet
Sending and Receiving
Use this WebSockets reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Sending Text
// Raw string ws.send("Hello!"); // JSON (most common pattern) ws.send(JSON.stringify({ type: "chat", text: "Hello", ts: Date.now() })); // Template literal ws.send(`ping:${Date.now()}`);
Sending Binary
ArrayBuffer / TypedArray
// Send a typed array const arr = new Uint8Array([0x01, 0x02, 0x03, 0xff]); ws.send(arr.buffer); // send the underlying ArrayBuffer ws.send(arr); // ws (Node) also accepts TypedArray directly // Structured binary with DataView const buf = new ArrayBuffer(8); const view = new DataView(buf); view.setUint32(0, 42, false); // big-endian view.setFloat32(4, 3.14, false); ws.send(buf);
Node Buffer
// Node.js server ws.send(Buffer.from("hello")); ws.send(Buffer.from([0x01, 0x02, 0x03])); ws.send(Buffer.alloc(16)); // zero-filled ws.send(Buffer.from(someString, "utf8"));
Blob (browser only)
const blob = new Blob([JSON.stringify(obj)], { type: "application/json" }); ws.send(blob);
Sending with a Callback (ws / Node)
// ws library: 3rd argument is a callback called after the data is flushed ws.send(data, { binary: false, compress: true }, (err) => { if (err) console.error("failed to send:", err); else console.log("sent and flushed"); });
Receiving Text
// Browser ws.addEventListener("message", ({ data }) => { if (typeof data === "string") { const msg = JSON.parse(data); // if JSON console.log(msg.type, msg.payload); } }); // Node (ws library) — data is Buffer by default ws.on("message", (data, isBinary) => { if (!isBinary) { const text = data.toString("utf8"); const msg = JSON.parse(text); } });
Receiving Binary
// Browser — prefer arraybuffer for direct byte access ws.binaryType = "arraybuffer"; ws.addEventListener("message", ({ data }) => { if (data instanceof ArrayBuffer) { const view = new DataView(data); const id = view.getUint32(0, false); const value = view.getFloat32(4, false); } }); // Node — data arrives as Buffer ws.on("message", (data, isBinary) => { if (isBinary) { const id = data.readUInt32BE(0); const value = data.readFloatBE(4); } });
Discriminating Message Types
String prefix
ws.on("message", (data) => { const text = data.toString(); if (text.startsWith("ping:")) return ws.send("pong:" + text.slice(5)); if (text.startsWith("msg:")) return handleChat(text.slice(4)); });
JSON envelope (most common)
// Both sides agree on { type, payload } shape ws.on("message", (raw) => { const { type, payload } = JSON.parse(raw.toString()); switch (type) { case "join": handleJoin(payload); break; case "chat": handleChat(payload); break; case "leave": handleLeave(payload); break; default: console.warn("unknown type:", type); } });
First-byte opcode (binary protocol)
ws.on("message", (data, isBinary) => { if (!isBinary) return; const op = data[0]; // first byte is opcode switch (op) { case 0x01: handleMove(data.slice(1)); break; case 0x02: handleShot(data.slice(1)); break; } });
Fragmented / Large Messages
The ws library reassembles fragments automatically. For very large payloads, stream instead:
// Streaming a large file over WebSocket (Node ws) import { createReadStream } from "fs"; const stream = createReadStream("large-file.bin"); stream.on("data", (chunk) => { if (ws.readyState === WebSocket.OPEN) ws.send(chunk); });
Backpressure — Don't Flood the Socket
// Check bufferedAmount before sending (browser) function drainSend(ws, messages) { for (const msg of messages) { if (ws.bufferedAmount > 65536) { // defer remaining messages setTimeout(() => drainSend(ws, messages.slice(messages.indexOf(msg))), 50); return; } ws.send(msg); } } // Node ws — pause/resume the stream ws.pause(); // stop firing message events // ... do slow async work ... ws.resume(); // resume
Sending JSON Safely
function send(ws, type, payload) { if (ws.readyState !== WebSocket.OPEN) return; try { ws.send(JSON.stringify({ type, payload, ts: Date.now() })); } catch (err) { console.error("send error:", err); } } send(ws, "chat", { text: "Hello", room: "general" });
Receiving All Messages — Server Broadcast
// Echo everything back to sender wss.on("connection", (ws) => { ws.on("message", (data, isBinary) => { ws.send(data, { binary: isBinary }); // echo in same format }); });
Binary Encoding Formats
| Format | Library | Use case |
|---|---|---|
| Raw bytes | — | Simple fixed-layout structs (game state) |
| JSON text | built-in | Flexibility, debuggability |
| MessagePack | msgpackr, @msgpack/msgpack | Compact JSON-equivalent binary |
| Protocol Buffers | protobufjs | Strongly-typed, versioned schemas |
| CBOR | cbor-x | Self-describing binary, like JSON |
| FlatBuffers | flatbuffers | Zero-copy access, gaming/HFT |
Gotchas
- In the browser,
send()withreadyState !== OPENthrowsInvalidStateError— always guard. - Node
wsmessagedata isBuffer, notstring— always call.toString()for text or checkisBinary. JSON.parsethrows on malformed input — wrap intry/catchfor untrusted data.- Large binary payloads should use streams or chunking; a single 100 MB
send()will queue the entire buffer before flushing. ws.send()is synchronous in terms of queueing but asynchronous in terms of I/O — use the callback orbufferedAmountto know when data has left the kernel buffer.