> The quick rule: if you need bidirectional, low-latency communication (chat, collaboration, games), WebSocket; if you only push from the server, SSE is simpler and cheaper to operate.
For most apps just use SSE and the built-in code for making HTTP requests (Fetch) instead of hacking up your own client side JS to make requests over a WebSocket. The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
Maybe if you are making many client requests per second there is an advantage to not sending full headers/cookies/etc... on each request but not if you're sending requests in response to user clicks/touches.
Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
> The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
In my experience this isn’t true; firstly you’re relying on an implementation detail of the platform that you’re executing on, of which you have no control over on the client side. Secondly, even if you aren’t opening a new connection per request, you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol - effectively a length and a mask to get the contents, rather than some (in http1 land) fuzzy parser.
If you can guarantee you’re hitting http2 or http3 then you might be closer in latency, but due to the complexity of both I would imagine plain http1 negotiated persistent WebSockets provide the best latency.
> Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
Sound advice otherwise, but you could have left this part out of the comment :D
You underestimate how many "sufficiently complicated SPAs" are slapped together low-code projects. Their maintainers have no idea what you're even talking about.
> Less traffic and less latency per action: a single persistent connection avoids repeating the TCP handshake and the HTTP headers on every interaction.
You don’t need a TCP connection for everything.
If you’re optimizing for that you can consider client-side caching which you can instruct using cache headers that every browser support. That usually reduces heavy hitters by a lot, even if you set the browser TTL to 1 minute which is fine for most of the scenarios.
Close but htmx with SSE and dom swaps and morphing gets you there without reinventing any wheels.
Pretty much every web app I build has this pattern in it from day 1, as they all quickly expand to have a realtime inbox and notifications subsystem to support workflows and agents.
What’s wrong with this idea, really? It’s redundant because you can serve HTML to requests with Apache or Ngnix or any other server on the happy path.
What’s right with the idea, really? It’s exactly what the tried and true preferences of developers have been shown to be: getting in the way of the happy path for no reason.
Now you can have build steps and put story points in Jira and do it all on the server where we don’t have to see it, and the success condition is that the text gets served. Both sides can be happy now.
> What’s wrong with this idea, really? It’s redundant because you can serve HTML to requests with Apache or Ngnix or any other server on the happy path.
Overhead, Security, Denial of service to name a few.
You now have two states to track. Is the web-server serving the current version that the web socket is rendering?
Is your monitoring enough? Is your monitoring going to detect if the web-socket server is suddenly taken offline? How do you monitor your web server is alive and ready to serve requests in the time of need?
How do you determine if the socket server is actual offline and not frazzled itself in an event-loop? A stray network packet you never conditioned it for.
If both your web-socket server and the web-server are going to follow the same source of truth for fail-over why not just use the web-server?
The web-server a tried and true method of serving websites. An application that is easier to load balance, tune and enhance with all the other security bells and whistles.
The best part is than you can actually serve a website without requiring JavaScript.
If enterprise corporate firewalls are silently blocking web sockets, how do you determine this? Any requests you're going to be rendering return blank back to the client. There isn't any easy way to determine if the client is blocked. How do you let the clients report an issue if they can't render the support page?
The starting sequence of a web socket is an HTTP request header to an upgrade the connection. Enterprise or DPI firewalls like blocking these upgrade headers. So for all you know the connection has been made but fails render silently.
You still need a service to serve the JavaScript fronted so unless you create a WebSocket HTTP server which you've then opened a can of worms; you have a perfectly functional web-server sitting around idly wasting resources.
As I bombard your web-socket server with faux requests slowloris style. Your web-server is alive and as far as it knows your socket server is alive too, how do you determine if the web socket is actually under attack? This adds more complexity in the mix.
Overall, it's not wrong. For a hobby project maybe but still a waste of time, the resources and the overhead for it all just isn't worth it.
Furthermore any alterations need testing on both parts. If you were to apply a hotfix for the web-socket side, does this negatively effect the web-server side?
Does it perform the same way in Firefox and Chrome? If Firefox is slower at parsing the JavaScript html json, how are you going to accommodate that?
Responsive site built via server-side render, with a particular benefit for partial page updates. 90% of the benefit of an SPA but 10% of the code: no api, no moving of system of record for state back and forth between database and browser (it's always db), etc. And you can avoid react.
A common use case: eg in Rails, a user has a table open. you can stream new records to the top of that table as they are created in ~5 lines of ruby (a broadcast on the model, and put the table rows in a turbo_frame with a turbo_stream_from somewhere on the page).
There are real limits to this -- you have to hold the update dependency graph in your head -- but the benefits are huge for small to medium amounts of responsiveness.
My experience has been great with Rails/Turbo and htmx.
I get how there's virtually no API if your client is simply rerendering the entire page each time it gets a ws message, but the article glosses over the partial page updates and just says "place the HTML where it belongs." How would that work? You'd need some kind of API for the client to request page parts and/or the server to tell the client where to place them.
Listed under advantages in the article: "State lives on the server. It is not memoryless request-response: there is a process per connected client that remembers where it is. It is the opposite of htmx, which is deliberately stateless."
> The quick rule: if you need bidirectional, low-latency communication (chat, collaboration, games), WebSocket; if you only push from the server, SSE is simpler and cheaper to operate.
For most apps just use SSE and the built-in code for making HTTP requests (Fetch) instead of hacking up your own client side JS to make requests over a WebSocket. The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
Maybe if you are making many client requests per second there is an advantage to not sending full headers/cookies/etc... on each request but not if you're sending requests in response to user clicks/touches.
Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
> The latency is the same because modern browsers multiplex HTTP requests over a single TCP connection that is left open.
In my experience this isn’t true; firstly you’re relying on an implementation detail of the platform that you’re executing on, of which you have no control over on the client side. Secondly, even if you aren’t opening a new connection per request, you’re still travelling through an entire HTTP stack implementation rather than the incredibly simple WebSocket protocol - effectively a length and a mask to get the contents, rather than some (in http1 land) fuzzy parser.
If you can guarantee you’re hitting http2 or http3 then you might be closer in latency, but due to the complexity of both I would imagine plain http1 negotiated persistent WebSockets provide the best latency.
> Any sufficiently complicated SPA contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Fetch.
Sound advice otherwise, but you could have left this part out of the comment :D
You underestimate how many "sufficiently complicated SPAs" are slapped together low-code projects. Their maintainers have no idea what you're even talking about.
> Less traffic and less latency per action: a single persistent connection avoids repeating the TCP handshake and the HTTP headers on every interaction.
You don’t need a TCP connection for everything.
If you’re optimizing for that you can consider client-side caching which you can instruct using cache headers that every browser support. That usually reduces heavy hitters by a lot, even if you set the browser TTL to 1 minute which is fine for most of the scenarios.
Close but htmx with SSE and dom swaps and morphing gets you there without reinventing any wheels.
Pretty much every web app I build has this pattern in it from day 1, as they all quickly expand to have a realtime inbox and notifications subsystem to support workflows and agents.
A good response to this post
https://yagni.club/3mstlyuxe5s26
Definitely a well reasoned counter point to choosing websockets over SSE.
I forgot to link to OP's response to the response. It's mostly Ai slop.
https://en.andros.dev/blog/bd74e61a/were-fighting-over-the-w...
Excellent article. More compelling than TFA being discussed. Thanks for sharing!
God damn
Love how DHTML, ASP.NET Ajax, JSF Ajax kind of keeps being re-invented.
What’s wrong with this idea, really? It’s redundant because you can serve HTML to requests with Apache or Ngnix or any other server on the happy path.
What’s right with the idea, really? It’s exactly what the tried and true preferences of developers have been shown to be: getting in the way of the happy path for no reason.
Now you can have build steps and put story points in Jira and do it all on the server where we don’t have to see it, and the success condition is that the text gets served. Both sides can be happy now.
Article should've mentioned caching, or lack thereof
> What’s wrong with this idea, really? It’s redundant because you can serve HTML to requests with Apache or Ngnix or any other server on the happy path.
Overhead, Security, Denial of service to name a few.
You now have two states to track. Is the web-server serving the current version that the web socket is rendering?
Is your monitoring enough? Is your monitoring going to detect if the web-socket server is suddenly taken offline? How do you monitor your web server is alive and ready to serve requests in the time of need?
How do you determine if the socket server is actual offline and not frazzled itself in an event-loop? A stray network packet you never conditioned it for.
If both your web-socket server and the web-server are going to follow the same source of truth for fail-over why not just use the web-server?
The web-server a tried and true method of serving websites. An application that is easier to load balance, tune and enhance with all the other security bells and whistles.
The best part is than you can actually serve a website without requiring JavaScript.
If enterprise corporate firewalls are silently blocking web sockets, how do you determine this? Any requests you're going to be rendering return blank back to the client. There isn't any easy way to determine if the client is blocked. How do you let the clients report an issue if they can't render the support page?
The starting sequence of a web socket is an HTTP request header to an upgrade the connection. Enterprise or DPI firewalls like blocking these upgrade headers. So for all you know the connection has been made but fails render silently.
You still need a service to serve the JavaScript fronted so unless you create a WebSocket HTTP server which you've then opened a can of worms; you have a perfectly functional web-server sitting around idly wasting resources.
As I bombard your web-socket server with faux requests slowloris style. Your web-server is alive and as far as it knows your socket server is alive too, how do you determine if the web socket is actually under attack? This adds more complexity in the mix.
Overall, it's not wrong. For a hobby project maybe but still a waste of time, the resources and the overhead for it all just isn't worth it.
Furthermore any alterations need testing on both parts. If you were to apply a hotfix for the web-socket side, does this negatively effect the web-server side?
Does it perform the same way in Firefox and Chrome? If Firefox is slower at parsing the JavaScript html json, how are you going to accommodate that?
> Simple, elegant and fast.
Until someone bombs your websocket server and you then have nothing at all.
Is this satire? Rage bait? Why would you ever do this?
Just make a normal website!! You've invented an MPA with extra steps!
Responsive site built via server-side render, with a particular benefit for partial page updates. 90% of the benefit of an SPA but 10% of the code: no api, no moving of system of record for state back and forth between database and browser (it's always db), etc. And you can avoid react.
A common use case: eg in Rails, a user has a table open. you can stream new records to the top of that table as they are created in ~5 lines of ruby (a broadcast on the model, and put the table rows in a turbo_frame with a turbo_stream_from somewhere on the page).
There are real limits to this -- you have to hold the update dependency graph in your head -- but the benefits are huge for small to medium amounts of responsiveness.
My experience has been great with Rails/Turbo and htmx.
I get how there's virtually no API if your client is simply rerendering the entire page each time it gets a ws message, but the article glosses over the partial page updates and just says "place the HTML where it belongs." How would that work? You'd need some kind of API for the client to request page parts and/or the server to tell the client where to place them.
So a RESTless webserver
In what way is it restless?
Listed under advantages in the article: "State lives on the server. It is not memoryless request-response: there is a process per connected client that remembers where it is. It is the opposite of htmx, which is deliberately stateless."
What happened to the live chat xD
great one