WebSockets Cheatsheet
Server with ws (Node)
Use this WebSockets reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Installation
npm install ws
npm install --save-dev @types/ws # TypeScriptPackage: `ws` — the de-facto Node.js WebSocket library. Zero runtime dependencies.
Standalone Server
import { WebSocketServer } from "ws"; const wss = new WebSocketServer({ port: 4000 }); wss.on("connection", (ws, req) => { const ip = req.socket.remoteAddress; console.log(`Client connected from ${ip}`); ws.on("message", (data, isBinary) => { const text = isBinary ? data : data.toString(); console.log("received:", text); }); ws.on("close", (code, reason) => { console.log("closed:", code, reason.toString()); }); ws.on("error", (err) => { console.error("ws error:", err); }); ws.send("Hello from server"); }); wss.on("error", (err) => console.error("server error:", err)); wss.on("listening", () => console.log("WebSocket server on :4000"));
WebSocketServer Constructor Options
| Option | Type | Default | Description |
|---|---|---|---|
port | number | — | Bind to this port (creates internal HTTP server) |
server | http.Server | — | Attach to existing server (share port) |
host | string | "0.0.0.0" | Bind address |
path | string | — | Only accept upgrades on this path |
maxPayload | number | 104857600 | Max message size in bytes (100 MB) |
backlog | number | 511 | TCP listen backlog |
clientTracking | boolean | true | Maintain wss.clients Set |
perMessageDeflate | boolean|object | false | Enable permessage-deflate compression |
skipUTF8Validation | boolean | false | Skip UTF-8 validation for text frames |
handleProtocols | Function | — | Subprotocol negotiation callback |
verifyClient | Function | — | Reject connections before upgrade (discouraged — see below) |
noServer | boolean | false | Manual upgrade handling |
WebSocket Instance Properties and Methods
Every ws object passed to connection event:
Properties
| Property | Type | Description |
|---|---|---|
ws.readyState | number | CONNECTING=0 OPEN=1 CLOSING=2 CLOSED=3 |
ws.protocol | string | Negotiated subprotocol |
ws.url | string | (client only) The URL passed to constructor |
ws.bufferedAmount | number | Bytes queued but not yet sent |
ws.binaryType | string | "nodebuffer" (ws) / "blob"|"arraybuffer" (browser) |
ws.isPaused | boolean | Whether the stream is paused |
Methods
| Method | Description |
|---|---|
ws.send(data[, options][, cb]) | Send a message |
ws.close([code[, reason]]) | Initiate close handshake |
ws.terminate() | Destroy the socket immediately (no close frame) |
ws.ping([data][, mask][, cb]) | Send a ping frame |
ws.pong([data][, mask][, cb]) | Send a pong frame |
ws.pause() | Pause receiving messages |
ws.resume() | Resume receiving messages |
ws.send() Options
ws.send("hello"); // text ws.send(Buffer.from([0x01, 0x02])); // binary ws.send(data, { binary: true }); // force binary ws.send(data, { compress: false }); // skip per-message deflate ws.send(data, { fin: false }); // start fragmented message ws.send(data, { fin: true }); // last fragment ws.send(data, (err) => { // delivery callback if (err) console.error("send failed:", err); });
WebSocketServer Properties and Methods
| Property/Method | Description |
|---|---|
wss.clients | Set<WebSocket> of all open connections |
wss.address() | { address, family, port } |
wss.close([cb]) | Stop accepting connections; cb fires when all closed |
wss.handleUpgrade(req, socket, head, cb) | Manual upgrade (noServer mode) |
wss.shouldHandle(req) | Returns true if the server should handle the request |
verifyClient — Reject Before Upgrade (discouraged)
The
wsmaintainers discourageverifyClientand will not extend it — the recommended pattern isnoServer: trueplus the HTTP server'supgradeevent: authenticate the request yourself,socket.destroy()(or write a raw HTTP response) on failure, and callwss.handleUpgrade()only on success. See the Sharing a Port with Express page for the full pattern.
const wss = new WebSocketServer({ port: 4000, verifyClient({ origin, req, secure }, callback) { const token = new URL(req.url, "http://x").searchParams.get("token"); if (!isValidToken(token)) { callback(false, 401, "Unauthorized"); } else { callback(true); } }, });
verifyClientis called synchronously when given one argument, asynchronously with two arguments (preferred).
Per-Message Deflate (Compression)
const wss = new WebSocketServer({ port: 4000, perMessageDeflate: { zlibDeflateOptions: { chunkSize: 1024, memLevel: 7, level: 3 }, zlibInflateOptions: { chunkSize: 10 * 1024 }, clientNoContextTakeover: true, // saves memory, reduces ratio serverNoContextTakeover: true, serverMaxWindowBits: 10, concurrencyLimit: 10, // max concurrent zlib ops threshold: 1024, // only compress if > 1 KB }, });
Compression helps for text-heavy JSON payloads (50–70% reduction). Disable for already-compressed binary data (images, video).
Server Events Reference
| Event | Callback signature | When |
|---|---|---|
connection | (ws, req) | New client connected |
error | (error) | Server-level error |
listening | () | Server bound and ready |
close | () | Server closed |
headers | (headers, req) | Before upgrade response (add custom headers) |
headers — Add Custom Response Headers
wss.on("headers", (headers, req) => { headers.push("Set-Cookie: session=abc; HttpOnly; SameSite=Strict"); headers.push("X-Custom: value"); });
Client (ws) Events Reference
| Event | Callback signature | When |
|---|---|---|
open | () | Connection established |
message | (data, isBinary) | Message received |
close | (code, reason) | Connection closed |
error | (error) | Error occurred |
ping | (data) | Ping received |
pong | (data) | Pong received |
unexpected-response | (req, res) | Non-101 upgrade response (ws client mode) |
upgrade | (response) | HTTP upgrade response received |
ws as a Client (Node-to-Node)
import WebSocket from "ws"; const ws = new WebSocket("wss://api.example.com/ws", { headers: { Authorization: `Bearer ${token}` }, // allowed in Node, not browser handshakeTimeout: 5000, rejectUnauthorized: true, // validate TLS cert (true in prod) perMessageDeflate: false, }); ws.on("open", () => ws.send("hello")); ws.on("message", (data) => console.log(data.toString())); ws.on("error", console.error); ws.on("close", (code) => console.log("closed:", code));
Checking readyState Before Sending
function safeSend(ws, data) { if (ws.readyState === WebSocket.OPEN) { ws.send(data); } }
Gotchas
ws.terminate()drops the TCP connection without a close frame — the client sees code1006. Usews.close()for graceful shutdown.wss.clientscontains all WebSockets, including those inCLOSINGstate — checkreadyState === WebSocket.OPENbefore sending.messagedata is aBufferby default in Node; call.toString()for text or setws.binaryType = "arraybuffer"forArrayBuffer.- The
errorevent must be handled — an unhandlederrorevent crashes the Node process. clientTracking: falseis a micro-optimization; only use it when you maintain your own set to avoid iteratingwss.clients.