JSON codec

How frames are serialised, what is validated, and why the wire is text.

Encode and decode

encode(frame) // → JSON.stringify(frame)
decode(raw)   // → JSON.parse(raw), then check `op`

That is the whole codec. decode requires the result to be an object with a numeric op in the known set; anything else — malformed JSON, a missing op, an opcode from a future version — raises a ProtocolError.

What is not validated

Everything else. There is no schema check beyond op, and extra fields survive the typed cast rather than being stripped.

That is a deliberate trade. Frames are validated by the handler that acts on them, where the error can say something useful about the operation; a schema layer in between would duplicate those checks and answer with a message about shapes instead. It also means a client sending a field a newer server understands is not rejected by an older one.

Text only

The uWebSockets gateway ignores binary WebSocket frames. Only text JSON is accepted.

A client that sends a Buffer or an ArrayBuffer will see no error and no Ack: the frame is dropped before anything looks at it. If a publish never resolves and the connection is healthy, check what your WebSocket library sends by default — several send binary for anything that is not a string.

Why JSON

The v1 drop-in path prefers debuggability over wire size. You can read a Blackevin session in devtools, paste a frame into curl, and diff two of them by eye. Against the messages most applications actually send, the difference against a packed binary format is smaller than it looks.

The codec is isolated behind encode / decode precisely so a binary format can be added later without any of the fanout, routing or storage code learning about it.

Writing your own client

The wire contract is:

  1. open a WebSocket to the endpoint with the credential in the query string
  2. wait for Connected (op 12) and keep the connectionId
  3. send text JSON frames; put an id on anything you want acknowledged
  4. match Ack (9) and Nack (10) back to your requests by that id
  5. answer Heartbeat (11) with a Heartbeat

Opcodes has the frame shapes and the Nack codes.

One thing that catches people: Message frames are not correlated to anything you sent, so they arrive with no id. Do not route inbound frames purely by id — switch on op first.

On this page