WebSockets Cheatsheet
Server with ws (Node)
Use this WebSockets reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
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.