Posting over the WebSocket
The WebSocket is open in both directions, and Priya’s client already sends typing events on it. Should Priya’s messages also go over it, instead of POST /rooms/12/messages?
The case for the WebSocket is that the connection is already open and already authenticated. Posting a message would be one more frame.
The case for REST is that a message sent on a WebSocket has no response, the way an HTTP request does. With POST /rooms/12/messages, Priya’s client gets three things that we would otherwise have to build ourselves:
- A confirmation. The
201 Createdresponse carries the new message with its identifier. - Errors. If Priya has been removed from the room, the server responds with
404. - A safe retry. The
Idempotency-Keyheader keeps a retry from posting the message twice.
What would building these ourselves involve? The server can still answer a message sent on a WebSocket. It learned at the handshake whose connection each WebSocket is, so it can send a frame back to Priya’s client alone.
- For a confirmation, the server sends Priya’s client a frame that says the message was saved, with its identifier. Priya’s client may have sent several messages, so each one it sends carries a number of its own, and the reply repeats that number. That way the client knows which of its messages was saved.
- For errors, we define our own error codes and put them in those replies, in place of HTTP’s status codes.
- For a safe retry, the client decides how long to wait for a reply before it sends the message again. The server still needs something like an idempotency key, so that it does not save the message twice.
That is request and response, rebuilt on top of the WebSocket. HTTP already gives us all of it.
A POST also runs the same validation and authorization code as every other request.
My preference is to keep posting on the REST API and use the WebSocket to receive new messages. The WebSocket carries only messages that are posted while it is open. Reading a room’s existing messages, when Marcus opens the room or scrolls up, stays on the REST API. Typing events go over the WebSocket. They are not stored, and nothing breaks if one is lost. If Priya is still typing, the next one follows within 3 seconds.
Discord makes the same split. Its developer documentation says that its WebSocket gateway lets apps “receive events” and that messages are sent through its HTTP API.
Here is the exchange for Priya’s next message:
sequenceDiagram
participant P as Priya's client
participant S as Server
participant M as Marcus's client
P->>S: WebSocket opened at /courses/7/live
M->>S: WebSocket opened at /courses/7/live
P--)S: typing, room 12
S--)M: typing, Priya Shah
P->>S: POST /rooms/12/messages
S-->>P: 201, message 61851
S--)M: message.created, 61851