4 ms·
Deprecate GraphQL Subscriptions over WebSockets in Favour of SSE/EventSource API
In this post, I'll discuss the pros and cons of using WebSockets to implement GraphQL Subscriptions. I'll then propose why we should consider deprecating the WS implementation in favour of a much simpler to use API, namely EventSource. Would love to hear your opinion!
https://wundergraph.com/blog/deprecate_graphql_subscriptions_over_websockets
- jondubois 6y agoBased on my experience, SSEs add more complexity and brittleness to applications compared to WebSockets. Having a single WebSocket connection to handle both RPCs and subscriptions is great because it binds all data flows to the lifecycle of a single socket - So if that single connection fails, it's easy to identify the disconnection and recover from it. Another major benefit of WebSockets is that you can control the exact timing of RPCs and subscriptions without having to worry about race conditions when the server expects to receive actions in a specific order. Also, SSEs add a lot of overhead because you need to authenticate each SSE channel independently (unlike with WebSockets where you only need authenticate a socket once at the beginning and then use it to subscribe to almost unlimited channels); so if you have many SSE EventSources, establishing them will waste a lot of resources since you need to pass the auth token (or session ID) to each EventSource that you open - In general, SSE authentication is tricky and forces you to rely on cookies which can be a problem in a lot of situations (e.g. on mobile if the front end is loaded the local file system as part of a WebView, your cookies will not be sent to the server due to cross-origin restrictions). Moreover, the lack of control over the lifecycle of the SSE connection makes it difficult to coordinate recovery from network or server failure/restart; a common problem happens when your server crashes and then all the SSE event sources try to reconnect immediately at the same time and this DDoSes your server again, then the reboot and crash cycle repeats indefinitely... With WebSockets, you can control the reconnect algorithm to add exponential backoff, for example (it can be customized to your exact requirements). Finally, the statefulness of WebSocket connections can be a huge advantage; you can store data pertaining to a single active client in-memory on the server-side which can be convenient for a lot of use cases (and efficient). For example, you can attach an auth token on the back end socket (in memory) and can use it to quickly check access rights for any channel without having to make an additional database call. SSEs are not a practical abstraction IMO. It's sad that people don't realize how good the WebSocket standard is. It's good because it's simple; it's also what makes it so flexible. WebSockets cover more use cases. Subscriptions as a concept have to support too many use cases (too generic) to be implemented with something as restrictive as SSEs.
- jensneuse 6y ago> Another major benefit of WebSockets is that you can control the exact timing of RPCs and subscriptions without having to worry about race conditions when the server expects to receive actions in a specific order. This is not related to GraphQL. > Also, SSEs add a lot of overhead because you need to authenticate each SSE channel independently (unlike with WebSockets where you only need authenticate a socket once at the beginning) I wouldn't consider this a lot of overhead. You're re-using an open TCP connection. You have to send a token which needs to be validated, sure. That's a sub millisecond overhead. > In general, SSE authentication is tricky and forces you to rely on cookies You can send a token as query parameter. Using TLS this is no different than a Header. > Moreover, the lack of control over the lifecycle of the SSE connection makes it difficult to coordinate recovery from network or server failure/restart; a common problem happens when your server crashes and then all the SSE event sources try to reconnect immediately at the same time and this DDoSes your server again, then the reboot and crash cycle repeats indefinitely... With WebSockets, you can control the reconnect algorithm to add exponential backoff (can be customized to your exact requirements). The EventSource API allows you to close the connection at any time. It will automatically reconnect which I consider a feature. There's also a "onopen" and "onerror" callback. You can use these to control reconnect behaviour and implement an exponential backoff strategy. I don't see the difference between WebSockets and SSE in case of a server Crash. Both will reconnect and DDos the server. > SSEs are not a practical abstraction IMO. It's sad that people don't realize how good the WebSocket standard is. It's good because it's simple; it's also why it's so flexible. WebSockets covers more use cases. You're absolutely right. However, we don't need this flexibility for GraphQL Subscriptions. I feel like what you're saying makes sense in general. However, in the use case of GraphQL Subscriptions I don't see advantages using WebSockets over SSE.
- jbramble81 6y agoI have to agree - with GraphQL subscriptions you don’t need full duplex. You are just subscribing.
- jondubois 6y ago>> I wouldn't consider this a lot of overhead. Depends on how many subscriptions you want. If you have 100s of subscriptions, it can add up. >> You can send a token as query parameter. If using JWT or some other kind of stateless signed (or encrypted) token, that would take up a large part of the URL. Also URL length is limited to around 2000 characters in some browsers. So it encourages short session IDs instead of signed tokens (session IDs are not stateless; so it's going to force an additional database call to be made on the server-side). >> The EventSource API allows you to close the connection at any time. It will automatically reconnect which I consider a feature. Yes there is some control. But if it auto-reconnects too fast, then your servers could end up DDoSed if you had a lot of concurrent clients. If it auto-reconnects too slow, then your users could miss more messages than would be desirable depending on the use case. With WebSockets, you can control the backoff to your needs and you can randomize, so you can ensure that not all clients try to reconnect at the exact same time; you can tailor it to your architecture. >> However, we don't need this flexibility for GraphQL Subscriptions. It seems like it simplifies things but also potentially reduces flexibility and performance. But I'm not familiar with the typical use case for GraphQL so maybe it's worth it.
- yladiz 6y ago> WebSockets are an outdated technology deemed dead thanks to the ietf not adding HTTP/2 support This is the first sentence, and I’m dubious - not about the lack of HTTP/2 but that WebSockets are dead, because I’ve never heard this until now. Is this really the case?
- jensneuse 6y agoIf your website is HTTP/2 all the way, do you really want to add WebSockets which force the browser to initiate another TCP connection? The EventSource or Streams API can easily replace WebSockets for most use cases.
- pornel 6y agoOn the Web nothing really dies. However, WebSockets are from the HTTP/1 era. They were PITA to support even in HTTP/1 servers, and they're even more annoying in HTTP/2 deployments.
- akvadrako 6y agoTo the OP - you are not supposed to include any text in a submission with a link. The text box is really only for 'Ask HN' and things like that.
- jensneuse 6y agoYou're right. Maybe a mod can change this? Sorry!
- ChrisArchitect 6y agoplease post this as a link post if you're sharing a link!
- Graphoppne 6y agoReally nice
- rlili 6y agoThere's this: https://github.com/CodeCommission/subscriptions-transport-sse https://github.com/CodeCommission/subscriptions-transport-ss... Although it doesn't seem to be maintained anymore.