I'm the author of this article and would appreciate any feedback/thoughts. I included just a few examples on the graphic (mostly from memory as I was lucky enough to come of age in 1990 so lived through it).
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 :)
So, there's an intermediate phase missing before AJAX. From roughly 1996 until the nascent stages of the AJAX era we used the Refresh attribute. This could be a <meta> tag in the header of the HTML content or you could send it as an HTTP header. While some sites refreshed the entire page this way, it was more useful for content served in frames/iframes (like headlines or sport scores or…urgh…advertising).
More pointedly, the Australian Open site (then ausopen.org, now apparently ausopen.com) used the Refresh: header to serve dynamic-ish scoring data and headlines circa 1997-1999 (I worked on the periphery of it through the 1999 open).
Using Reddit as an example of a good user interface is going to turn some people off. Arguably Reddit is an example of everything that goes wrong here. The interface is less informative and usable than it used to be so that it can get in people's faces with irrelevant updates and endless ads. And on top of that it tends to be slow and prone to going dead. Perhaps real time applications may in time through experience lead to real quiet and real politely well behaved applications?
Thanks for your feedback! I'm not sure I said it's a good user interface (I'm not a visual person so wouldn't have the confidence) but I do think it's a good example of a set of features that show a community interacting in realtime. Maybe I didn't make that clear in the article so I'll bear in mind for future writing. TY!
There is a concerning cargo cult feedback loop in the industry fueled by companies offering websocket based solutions and writing long articles about everything that this COULD be used for and young developers half heartedly looking for solutions and then defaulting to websockets for anything with some server push or realtime requirements. Websockets are bad advice for sending tennis-match data and reddit-comment counters to a client, websockets are bad advice for messenger/ chat applications and most other things used as an example. 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. (every 5+ seconds is not high frequency) If you are not building one of those chances are high you are being lured into that cargo cult. All you need is http2 and SSE or streaming fetch. Websocket companies will tell you all about how most problems are solved by their offerings or fancy frameworks but ignore the fact those problems could be not there in the first place. And also inexperienced developers that never operated websocket based solutions over a few years or millions of users will be very quick to lecture you about how they never had any problems or headaches, because its hard to admit that you skipped proper solutions research.
I mostly agree with this - right tool for right job.
> 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.
Disclaimer: I work for Ably.
I agree that it's not valid to claim that websockets are the best fit in every situation. If the requirement is simply to stream server to client events, in an application where that doesn't depend at all on having any per-client state, then you can serve those things statelessly with SSE or HTTP/2. However, much of the time, the thing that makes websocket implementations complex isn't anything to do with websockets per se, but the fact that the server has per-client state; this state needs to exist somewhere in the backend, and it needs to survive reconnections (and so can't simply be some ephemeral session state in the specific server handling a connection). In this case, you could be using HTTP/2, or comet over HTTP1, or websockets, and the main technical issues you will face scaling to millions of users are going to be essentially the same. Websockets (at least, until web transport is here) just give you a more convenient and efficient primitive.
This is perfect example for my remark. With HTTP/2 and SSE you use the exact same feature of your framework to have server side state that would solve this between requests or in any other situation, there is no new problem to solve! Apart from that fact, server side per connection state is usually an anti-pattern, not a common situation as you put it. As far as i can think right now, the only situations where this is required and cannot be solved with better caching would be the same bi directional high frequency streaming situations already mentioned.
So then how would you solve it without state? For someone like me who is new to the reading these comments I hear a lot of bickering / arguments but no provided solution
I've spent the last year working on an e-commerce project that esclusively uses websocket for real-time updates and communication. There are some great benefits to it, but websockets also introduce a lot of technical challenges that may not be obvious when you start. Things that come to mind are:
- 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
The need to support third parties is exactly why we ended up embarking on a phased deprecation of our websocket implementation.
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.
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
You should check out RxDB. It applies realtime replication to an offline first storage. This means you even have less backend load compared to regular CRUD apps. Also you get multi-tab support for free where only one browser tabs runs the client-server connection.
Thanks for your feedback. Could you elaborate a little? I'm not being snarky -- I came up with them based on similar things I'd seen online and I'm not great at visual communication, so if you've improvement suggestions, I'd genuinely like to hear them.
They would be a lot clearer if they showed how the messages actually look, with some example data.
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" }`.
If you're interested in building realtime web apps with Postgres and React, check out https://thin.dev/
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).
Even for those kinds of cases I've often found it's simpler to use a standard http API and a websocket notification to tell the client to fetch from the API when something relevant has changed. Then you can leverage all your existing auth and error handling.
33 comments
[ 2.9 ms ] story [ 83.2 ms ] threadI 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 :)
The timing feels a LOT better these days.
> 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.
- 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
https://rxdb.info/
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.
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).
I have never, not once in my entire life, needed a part of a web page to update independently.