GraphQL over Internet

Denis Badurina

What Is GraphQL?

Some of you have heard this question many times over… But for the beginners out there: GraphQL is a data language. And that’s it, nothing more and nothing less. It a system of communication that defines the grammar and the vocabulary. It helps you request the exact data you want, and also helps the server fulfill that request. Like in the real-world — those that know the language can communicate. Please keep in mind, GraphQL is not a database, nor is it a server - it is simply - just a language.

GraphQL Is like a Pizza Order

Imagine ordering a pizza - you call and ask for a pizza with special toppings, and then the person on the other end takes the order and tells the pizza chef what to make. Well, GraphQL is kinda’ like that phone call, except that instead of ordering pizza, you ask a server for some data; instead of speaking to a person, you’re sending a specially formatted message to a server called a GraphQL query. Just like you ask for specific toppings, you ask for specific data from a server; and just like the pizza chef makes your pizza with the exact toppings, the server responds to the GraphQL query with the exact data you queried.

Immediately we can see that there are two agreements on both sides:

  • You know you’re ordering pizza (and therefore you know which toppings you can choose from)
  • You also know that both you and the waiter speak the same language

Subsequently you know exactly what to expect when picking up your order: a pizza with the toppings you communicated over the phone.

However, neither of you knows, or cares, about how a telephone works!

Synonymously, GraphQL itself deliberately does not care about the transport - it doesn’t care about how you send the request, how you receive the response, and what is the delivery path — all of this is out of the GraphQL’s scope. It is quite literally just a data language.

GraphQL over HTTP

HTTP is the most common choice when transporting GraphQL because of its ubiquity. With this in mind, a group of superheroes formed in 2019 to solve exactly this problem. A working group of avid GraphQL lovers all with the same goal - standardize the process of transporting GraphQL over HTTP. The group mostly works asynchronously, but there are Zoom meetings, where progress is shared, and important topics are discussed.

This is a joint effort, so everyone’s opinion matters, and everyone is welcome to contribute!

There is already a specification being standardized named “GraphQL over HTTP”. At the time of writing, it is in the proposal stage. It currently describes exclusively single response operations, this is the main focus; so, no file uploads, no incremental deliveries, yet…

Reference Implementation

To accompany the spec, the reference implementation graphql-http is developed. It is a zero-dependency server, client and audit suite.

The client is very simple to use and offers some great niceties - one of them being silent retries of failed requests.

Beware that the library exclusively implements the current official “GraphQL over HTTP” spec. It does so in the clearest and most explicit manner so that others implementing their own server can quite literally use graphql-http as a reference of the spec.

The library is server agnostic and, as such, can run in any JavaScript environment. Additionally, it maintains the list of compliant servers and their reports. You’re more than welcome to send PRs with your own implementation!

graphql-http.com

Checking for compliance was never easier! graphql-http.com is a website for swiftly auditing servers - even those that are running locally! It is always up-to-date with the current spec.

Of course, security is very important - the website audits run exclusively in the browser, and there is no tracking, data sharing, or external dependencies! Meaning you can download the website on your computer and have a portable offline checker.

GraphQL over WebSocket

Before we talk about the current state of GraphQL WebSocket support, let’s start with a brief overview of the history. Initially, there was a subscriptions-transport-ws library that was built and maintained by Apollo. It also had an accompanying protocol. But as time passed, they haven’t been able to spend as much time on it — probably due to increased demand in other areas. The repo grew in issues, and PRs were never reviewed. Eventually, it became deprecated. However, this library actually kick-started and pushed the evolution. It was a big inspiration and a great influence for an upcoming refresh!

The WebSocket Protocol provides a persistent, full-duplex communication channel between the client and server, allowing data to be transmitted in real-time. Full-duplex is just a fancy word for bidirectional communication which means that the server and the client can exchange messages through the connection channel whenever they want. When used in conjunction with GraphQL, it allows clients to subscribe and receive events from the server in real time. This makes it ideal for applications that require real-time updates and push notifications.

The GraphQL over WebSocket protocol leverages all pros of the WebSocket and gives shine to real-time GraphQL. Please note that it is not backwards compatible with Apollo’s protocol behind subscriptions-transport-ws.

⚠️ Beware that the specification is currently an RFC and is open to changes!

Reference Implementation

A specification goes best with a reference implementation. Say hello to graphql-ws. It is a zero-dependency server and client, very simple and bare-bone. But don’t be mistaken, it’s very versatile and extendable!

A common struggle when working with WebSockets is re-connecting. graphql-ws is all about stable connections. Silent retries with custom strategies are easy to use and extend.

Oh and, it is completely server agnostic - run it on any JavaScript environment!

GraphQL over SSE

Traditionally, a web page has to send a request to the server to receive new data; that is, the page requests data from the server. With server-sent events, it’s possible for a server to send new data to a web page at any time, by pushing messages to the web page. These incoming messages can be treated as Events + data inside the web page.

MDN on Server-sent events

Why Not Just EventSource?

Before we proceed, you might ask why not just use the browser native EventSource? Do we even need a whole specification? Are we overthinking it?

That’s an excellent question, actually! Here are a few gotchas’ about the browser native EventSource:

  • Headers cannot be customized
  • If the HTTP response is erroneous and not a 200 OK, EventSource raises an ultra vague error
  • The requests cannot have an accompanying body
  • The retry mechanism is too simple, it will simply retry following a fixed interval
  • Server might limit the length of the URL
  • EventSource will keep reconnecting indefinitely if the server is the one that closes the
    • Meaning, if a subscription ends and the server closes the connection as a result - the browser will automatically reconnect

The Specification

Keeping everything from the previous section in mind, the GraphQL over SSE spec is created. It basically lifts all usual limitations of SSE while keeping the browser native EventSource in mind - so you’re not locked to a library, you’re still free to use it!

It supports two working modes:

⚠️ Beware that the specification is currently an RFC and is open to changes!

Distinct Connections Mode

The first mode I want to talk about is “distinct connection mode”. It’s pretty much what you’d usually do with SSE - each connection is a subscription on its own. The operation requests conform the the requests in the GraphQL over HTTP spec, but with two differences:

  • Content-Type header must have the text/event-stream value
  • All GraphQL errors must be reported through the SSE connection
    • So you first accept the connection and then through it you stream the error in form of a message. This allows EventSources to have proper error reporting with descriptive errors •
  • An additional complete event message that is used to indicate to the client that the subscription was completed from the sever
    • This makes EventSources aware when the subscription has ended server-side so that it can be closed and with that avoid unexpected reconnects

Single Connection Mode

The other mode of operating is called “single connection mode”. A often talked about limitation of SSE is HTTP/1. HTTP/1 powered servers are limited to only 6 active connections per domain, meaning you can only have 6 concurrently active subscriptions (compared to HTTP/2 powered servers that are by default limited to 100+ active connection per domain).

The “singe connection mode” creates a single connection to the server which is used exclusively for streaming events and messages. Then you issue separate HTTP requests to execute operations. This property makes it safe for HTTP/1 environments, but also for subscription heavy apps even in HTTP/2 environments (simply because subscriptions are typically best when really granular and this can sometimes exceed even the 100+ limit of HTTP/2).

These HTTP operation requests conform to the GraphQL over HTTP spec with just one difference: successful operation requests are responded with 202: Accepted and the results are streamed to the single established connection.

Reference Implementation

Of course, a reference implementation goes along with the spec. graphql-sse is a zero-dependency implementation of both the server and the client.

One cool thing is the fact that the server automatically detects the operation mode (single connection vs distinct connection) and behaves accordingly.

The client is pretty advanced too, it offers a few interesting elements like:

  • Custom retry strategies

  • You can decide exactly when you want to retry connecting (like you might have a health check, and only after the server becomes health do you want to retry)

  • Since graphql-sse is packing a custom implementation of SSE and is not using the browser native EventSource, you’re able to:

    • Set custom headers
    • Add a body to the request
    • Experience very descriptive errors

Like all of the libraries mentioned in the presentation - graphql-sse too is server agnostic - you can run it in any JavaScript environment!

WebSockets vs. SSEs

There’s an abundance of articles, tables and benchmarks on the internet comparing WebSockets to SSEs, so I’ll just briefly touch the obvious.

WebSocketsSSEs
CommunicationReal-time and bi-directionalTransported over simple HTTP instead
FirewallsHas troubles with corporate and outdated firewallsNo trouble with any firewall doing packet inspection
LatencyLower because of persisted TCP connectionCan be higher sometimes because of multiplexing
RetriesNeeds to be developed in user-landBrowsers have built-in retry mechanisms
SupportBrowser native through WebSocket. No custom implementations for the browserBrowser native through EventSource. But, can be custom implemented on fetch()

Presentation

Check out my presentation from GraphQL Zurich Meetup sponsored by SwissLife.

Join our newsletter

Want to hear from us when there's something new?
Sign up and stay up to date!

*By subscribing, you agree with Beehiiv’s Terms of Service and Privacy Policy.