Receiving Messages
1. Handling Message Events
| Event Field | Type |
|---|---|
event.data | string | Blob | ArrayBuffer |
event.origin | string |
event.target | WebSocket |
Example: Router by type
const handlers = { "chat.message": onChat, "presence": onPresence };
ws.addEventListener("message", (e) => {
const msg = JSON.parse(e.data);
handlers[msg.type]?.(msg.payload);
});
2. Parsing Text Messages
| Format | Parse |
|---|---|
| JSON | JSON.parse(e.data) |
| CSV/TSV | Custom split |
| Plain | Direct string |
3. Parsing JSON Messages
Example: Safe parse
function safeParse(raw) {
try { return JSON.parse(raw); }
catch { return null; }
}
| Concern | Mitigation |
|---|---|
| Malformed JSON | Wrap in try/catch |
| Prototype pollution | Avoid __proto__ keys |
| Large payloads | Stream / chunk |
4. Validating Message Format
Example: Zod schema
import { z } from "zod";
const ChatMsg = z.object({ "type": z.literal("chat.message"), "text": z.string().max(2000) });
const result = ChatMsg.safeParse(parsed);
if (!result.success) return reject(result.error);
| Library | Lang |
|---|---|
| zod | TS |
| valibot | TS |
| ajv (JSON Schema) | JS |
5. Handling Message Order
| Guarantee | WebSocket |
|---|---|
| In-order (single connection) | Yes (TCP) |
| Across reconnects | No — use sequence IDs |
| Across multiple sockets | No — single source-of-truth or seq |
6. Handling Binary Messages
Example: Branch on type
ws.binaryType = "arraybuffer";
ws.addEventListener("message", (e) => {
if (typeof e.data === "string") handleText(e.data);
else if (e.data instanceof ArrayBuffer) handleBinary(new DataView(e.data));
});
| binaryType | Incoming Type |
|---|---|
"blob" | Blob |
"arraybuffer" | ArrayBuffer |
7. Reading Blob Data
Example: Blob → ArrayBuffer
ws.addEventListener("message", async (e) => {
if (e.data instanceof Blob) {
const buf = await e.data.arrayBuffer();
processBinary(buf);
}
});
| Method | Returns |
|---|---|
blob.arrayBuffer() | Promise<ArrayBuffer> |
blob.text() | Promise<string> |
blob.stream() | ReadableStream |
8. Processing Typed Arrays
Example: Decode header + payload
function decode(view) {
const type = view.getUint8(0);
const len = view.getUint32(1, false); // big-endian
const body = new Uint8Array(view.buffer, 5, len);
return { type, body };
}
| Step | API |
|---|---|
| Wrap buffer | new DataView(buf) |
| Read fields | getUint8/16/32/Float32 |
| Slice | new Uint8Array(buf, offset, len) |
9. Implementing Message Queue
Example: Async iterator queue
function makeQueue(ws) {
const buf = [], waiters = [];
ws.addEventListener("message", (e) => {
if (waiters.length) waiters.shift()(e.data);
else buf.push(e.data);
});
return {
async next() {
if (buf.length) return buf.shift();
return new Promise(res => waiters.push(res));
}
};
}
| Pattern | Use |
|---|---|
| Async iterator | for-await loops |
| EventTarget bridge | Library boundaries |
| Observable | RxJS integration |
10. Handling Large Messages
| Technique | Detail |
|---|---|
| App-level chunking | Split into ordered frames with seq |
| Streaming reassembly | Maintain buffer per message ID |
| Out-of-band transfer | Use HTTP for large blobs, WS for notify |
Warning: Default
ws server maxPayload is 100 MiB. Tune both client and server limits to prevent close 1009.