The realtime web: evolution of the user experience
ably.com
ably.com
- Persistent connections make horizontal scaling more difficult
- Web Application Firewall don't usually support payload scanning for threats over websocket messages (eg. Cloudflare)
- If you need to integrate with third parties, you may end up needing a standard REST API anyway, since it's a lot less common to integrate via websockets (especially server-side). You then end up with two APIs. Also, websockets have less standard tooling for automatic documentation purposes such as Swagger/OpenAPI
- It's harder to load test the system, difficult to estimate the breaking point of the infrastructure via websockets
- HTTP Status Codes provide a standard error mechanism to HTTP server calls, while with websockets it's entirely up to you (which can be a good and bad thing)
- You need to manage explicitly situations where the connection drops and what to do when it recovers, as you may have lost some data in the meantime
- You give up a lot of standard mature tooling for caching and rate limiting
As it turned out barely anybody wanted to learn how to open a websocket connection and relearn all the oddities of a bespoke websocket connection (error handling, etc.) whereas with REST all these things are just more or less standardized.
That can do peak many-to-many so whatever usercase you need it will support it.
Never learn things that aren't final.
Using the Actor Model provided by Akka and an event sourced architecture does not fit very well with the request-response model of HTTP. Commands are usually issued in a fire-and-forget pattern, with resulting events being pushed to the client in a completely asynchronous way.
By using websockets via SignalR, we can better accomodate this pattern on the client code as well. On top of being a better fit with our backend architecture, it also provides some benefits in the frontend such as:
- Limited availability items disappear as soon as they actually are not available anymore, without the client having to refresh
- We can integrate more easily with payment gateways where payment confirmation usually arrives via a server side webhook. We can then easily push the "payment succeeeded" event to the client as soon as we receive it, without polling.
- Even the backoffice benefits from real-time, as all backoffice users are guaranteed that when they look up a customer/orders/products, they are looking at the most up-to-date information, even if somebody else edits the same record at the same time, or the record is updated by external APIs/scheduled job.
- We can provide easily real-time dashboboards that are listening to the feed of events being persisted
> They are mostly useful for things where high frequency bi-directional communication is required such as syncing multiple cursors, player positions/actions in realtime games and visual co-editing such as figma.
This is the only use case for websocket I have personally found - streaming user input to the server. Anything that goes server->client can just be some variant of fetch that is invoked as needed.
In my use cases, I stream player mouse events (via pointer lock API) over the socket. Depending on the browser/os/mouse, this can be hundreds per second.
I remember when Yahoo mail came along with AJAX and how astonishing it was to not have to refresh to get new mail. Although, over a dial up connection, I'm not sure how "realtime" anything was :)
Next to great user experience, building your application in a realtime way also allows to simplify the state management in certain parts.
A typical react app might use something like redux to store a subset of the application's database state locally. Now whenever you do any write operations to your data, you need to make an API call and also make sure the local redux state is kept in sync.
When you're embracing realtime everywhere, you avoid this, because any write operation automatically updates your realtime data, so you don't need to manually e.g. update your redux state. This allows you to skip a lot of the code that's needed for state management locally, so it saves a lot time and bugs (less code => less bugs).
The timing feels a LOT better these days.
For example the HTTP one could have `{ timeout: 60000 }` in its requests and stuff like `{ events: [{ type: "something_changed" }] }` in the responses.
And the WS messages could look something like `{ action: "send_message", content: "hi" }` and `{ event: "new_message", content: "hi" }`.
Just an idea.
I have never, not once in my entire life, needed a part of a web page to update independently.