Separate transport from application protocol
WebSocket provides an opening handshake and message framing over a connection. It does not define invoice subscriptions, delivery acknowledgements or event replay. Choose it when frequent communication in both directions supports the product experience, and define those higher level behaviors deliberately.
RFC 6455 requires client frames to be masked and server frames to be unmasked. Maintained WebSocket implementations handle these framing details. Application code should focus on its message contract rather than implementing a new frame parser or treating framing compliance as a complete security design.
Keep command and event messages distinct
Give client commands a request identifier so responses can be correlated without depending on arrival order. A subscription acknowledgement confirms the subscription state, not completion of a business action. Keep event identity and cursor information separate from a command's correlation identifier.
Validate message type, required fields and payload size before processing. Reject unknown commands predictably. Define subscription messages in the application protocol alongside success, rejection, unsubscribe and cursor expiration messages. WebSocket supplies the transport framing.
{
"type": "subscribe",
"requestId": "req_81",
"resource": "invoices/inv_1042",
"after": "cursor_2048"
}Rebuild session state after interruption
A network path may fail without an immediate close notification. Define liveness using transport support or an application heartbeat with a bounded timeout. Browser applications cannot directly send protocol ping control frames through the standard WebSocket interface, so application heartbeat messages need their own documented meaning.
Reconnect with capped backoff and jitter, then reestablish authorized subscriptions. A new connection does not resume the previous application state automatically. When replay is supported, validate the cursor and its retention window; otherwise retrieve current state and make the reset visible to the interface.
Protect capacity and test recovery
Bound connection counts, message size and pending sends. Establish a slow consumer policy that matches the data: replaceable status updates can be coalesced, while business events may need durable storage and later replay. Report dropped or deferred work instead of hiding it behind a healthy connection indicator.
Test reconnect storms, expired sessions, malformed messages, duplicate subscriptions and abrupt server restarts. Verify that subscriptions are cleaned up when views close and that a client cannot subscribe outside its scope. Monitor queue growth and message age alongside connection counts.