REST over WebSockets instead of HTTP

45,569 reads

These days, it’s practically impossible to talk about REST without also talking about HTTP — The two concepts are tightly intertwined; in fact, REST and HTTP 1.1 were developed in parallel by the same person (Roy Fielding). The verbs in HTTP (GET, POST, PUT, DELETE…) make it ideal for performing the kinds of stateless atomic operations required by REST.

Over the past couple of years, a few things have happened on the web which have disrupted the order of things:

FP front-end frameworks like React have gained a lot of traction and have introduced entirely new paradigms for loading data from a server and manipulating that data (see GraphQL).

WebSockets have become widely supported* thereby increasing the interest in real-time technology (see https://blog.baasil.io/why-you-shouldnt-use-long-polling-fallbacks-for-websockets-c1fff32a064a).

Together, these two events have created the perfect storm for the disruption of REST. Now that front-end frameworks have become very good at automatically rendering live data, the potential for updating that data in real-time (straight from the server) has suddenly become viable. Services like Firebase and Parse have gained a lot of traction on the back of this use case — Why should we force users to refresh the page to get the latest data when we can just as easily have that data update itself in real-time?

Data synchronisation services/frameworks like Firebase and Parse are convenient, but they are not ideal for a number of reasons:

They force you to structure your system’s data in a particular sub-optimal way.

They typically have limited querying capabilities — Making it hard to search through that data.

The synchronisation aspect is error-prone; data conflicts might not resolve correctly so you have to add hooks on the front-end to handle those cases — This could affect the correctness of your data if you’re not careful.

The synchronisation aspect is expensive (bandwidth and CPU-wise) and is not necessary for most use cases (where REST would normally be used).

You still need to have your own server(s) on the side to process sensitive business logic like emails and payments, so the entire data sync service is in fact redundant… These services go against the fundamental idea of dumb pipes and smart endpoints. Why not interact directly with the target server from your front-end using dumb pipes (WebSockets)?

https://crisp.im/blog/why-you-should-never-use-firebase-realtime-database/

As an alternative to data-sync services, there have been some implementations of subscription-based solutions for GraphQL (see http://graphql.org/blog/subscriptions-in-graphql-and-relay/ also; http://www.apollodata.com/).

On the REST side, there have been various implementations also based on the idea of real-time subscriptions (see REST Hooks http://resthooks.org/).

While the idea of REST Hooks is interesting, it was introduced a while ago and so it’s more heavily centred around HTTP than WebSockets — In REST Hooks, WebSockets are essentially treated as a progressive enhancement. However, now that WebSockets are more widely available, maybe the time has come to treat WebSockets as first-class citizens and to start introducing RESTful HTTP verbs to WebSockets (as naming conventions for events). This is very new territory, but there are some important benefits to using REST over WebSockets:

With WebSockets, you don’t need to rely on cookies for authenticating each action independently. You only need to authenticate once for each connection. If you use a WebSocket framework like SocketCluster, then this happens automatically as part of the handshake (it will pick up the JWT from localStorage). This mitigates the risk of CSRF attacks because individual actions do not need to carry authentication information**. Not having to send auth/session info with every request loosens your reliance on cookies—and getting the session ID or JWT from localStorage instead of cookies works better with mobile HTML5 frameworks like React Native, Ionic and Cordova***. WebSockets are many times more efficient than HTTP — Especially when you have small payloads — This may allow us to take REST one step further so that we could Create, Read, Update or Delete individual fields on a resource (instead of having to read or operate on the entire resource at once) — This is particularly relevant for real-time applications because updating fields individually removes the possibility of resource-level merge conflicts — That’s essentially the problem which GraphQL was created to solve (by allowing you to declare only affected fields), but if you factor-in WebSockets, we may find that REST is still a valid approach if we’re willing to increase the granularity. Sending 50 HTTP requests just to load the data (e.g. 10 resources with 5 fields each) for a specific screen is madness, but sending 50 short WebSocket frames is actually viable.

If you use a WebSocket framework that supports pub/sub (such as Faye or SocketCluster), having your front end data update in real-time is trivial****; you just need to subscribe to a relevant real-time channel and then request a snapshot of the related data whenever the subscription is activated (or reactivated after a lost connection). See https://blog.baasil.io/socketcluster-design-patterns-for-chat-69e76a4b1966 for details of how to do with with SocketCluster.

* There is a popular myth that WebSockets don’t work in corporate environments/networks — This applies when serving plain (unencrypted) ws:// traffic. If you serve your WebSockets over wss:// instead, then it will almost certainly work. See https://blog.baasil.io/why-you-shouldnt-use-long-polling-fallbacks-for-websockets-c1fff32a064a. Note that there may be extreme cases where companies might install fake root certificates on their corporate workstations to snoop in on their employees’ internet usage (MitM) and it’s possible that in these cases the corporate proxy could still block “encrypted” WebSocket traffic.

** Some developers will argue that cookies are superior because of the httpOnly flag but this article should clear things up: http://www.gnucitizen.org/blog/why-httponly-wont-protect-you/

*** When using HTML5 frameworks to build native/hybrid mobile apps, HTTP authentication with cookies doesn’t work because your .html files are usually sitting on the mobile device itself (so the local domain for the file won’t match the domain where your HTTP REST API is hosted; this would cause a CORS issue and so cookies will not be sent).

**** I wrote a sample app a while ago which uses this concept (using Google’s Polymer framework for components) https://github.com/socketcluster/sc-sample-inventory — All data in that app updates in real-time including lists (with support for filters and complex query-based transformations). This is not entirely new though; a lot of developers and companies have been using variations of the subscribe-and-snapshot approach long before this.

Tags