🥽 NestJS GraphQL WebSockets

Since the very first iteration of

ecoeats

, I've grappled with the support for WebSockets that comes as part of the

@nestjs/graphql

package. Trying to use WebSockets in a way that does not noticeably impact the user with intermittent reconnections due to token expiration is a serious challenge. I'll talk in this post about how I solved it within the confines of NestJS.

The Problem

A million requests a day. Ouch.

We currently operate in a single town, St Andrews, in Scotland. Our typical order numbers in a single day number the hundreds, with an average of 10+ delivery riders and 15+ merchants online at a given time.

So where do all the requests come from? Customers, Riders, Merchants and Admins. Every part of the ecoeats platform that looks real-time uses polling in order to get up-to-date info. From our ops dashboards requesting live capacity information, to Merchants' tablets that auto-refresh the list of orders, to a Rider's phone requesting live route information - it's polling all the way down.

Early on when building the client applications for the ecoeats platform, I was tempted to use WebSockets as a transport protocol just as a mechanism for subscriptions. The problem I'd run up against was frequent unauthorised responses as the JWT that initialised the session expired. This is due to the way that NestJS handles Guards on a per-request basis, checking for each query that the user is still authenticated regardless

Everything over WebSockets?

If you're trying to reduce polling with as little refactoring as possible, you can take a shortcut by piping every query and mutation down a WebSocket instead. This isn't as efficient as partial cache updates via Subscriptions, but it is a fantastic way to reduce your request count by utilising the already established connection.

Below is how we do this on our Ops dashboard. There are a few hacks in there to make sure we reconnect the WebSocket when logged in and when we log out, along with skipping the WebSocket when we need to do a file upload.

Success! ✨

Piping everything over websockets meant a huge decrease in HTTP overhead for the critical real time services like our Ops dashboard and Merchant tablets.

☕️ Made with Intenso