WebSocket Typed Message Protocol
Layer a typed message protocol over WebSocket with Zod parsing per message type, versioned envelopes, and safe close-frame handling.
Code
import { z } from 'zod';
import { WebSocket } from 'ws';
const Envelope = z.object({
v: z.literal(1),
type: z.string(),
payload: z.unknown(),
});
const Messages = {
'tick.update': z.object({ symbol: z.string(), price: z.number() }),
'alert.raise': z.object({ level: z.enum(['warn', 'critical']), text: z.string() }),
};
type MessageType = keyof typeof Messages;
function parseMessage(raw: string) {
const envelope = Envelope.safeParse(JSON.parse(raw));
if (!envelope.success) return { ok: false, reason: 'bad envelope' };
const schema = Messages[envelope.data.type as MessageType];
if (!schema) return { ok: false, reason: 'unknown type ' + envelope.data.type };
const payload = schema.safeParse(envelope.data.payload);
return payload.success
? { ok: true, type: envelope.data.type, payload: payload.data }
: { ok: false, reason: 'bad payload for ' + envelope.data.type };
}
const socket = new WebSocket('wss://stream.example.com/v1');
socket.on('message', (data) => {
const parsed = parseMessage(data.toString());
if (!parsed.ok) {
socket.close(1002, 'protocol violation');
return;
}
if (parsed.type === 'tick.update') {
console.log(parsed.payload.symbol, parsed.payload.price);
}
});
socket.on('close', (code, reason) => {
console.log('closed', code, reason.toString());
});Report a problem with this exampleOpens a pre-filled issue on GitHub.