GraphQL over Internet
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.
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.
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...
To accompany the spec, the
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
graphql-http as a reference of the spec.
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.
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
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
⚠️ Beware that the specification is currently an RFC and is open to changes!
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.
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.
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
- Headers cannot be customized
- If the HTTP response is erroneous and not a 200 OK,
EventSourceraises 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
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:
- "distinct connections mode" - basically the regular way you'd use SSEs, each request is a connection on its own
- "single connection mode" - a single SSE connection is maintained for streaming, but other accompanying requests dictate the behavior
⚠️ Beware that the specification is currently an RFC and is open to changes!
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-Typeheader must have the
- 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 •
- So you first accept the connection and then through it you stream the error in form of a message. This allows
- An additional
completeevent 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
- This makes
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.
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)
graphql-sseis 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
There's an abundance of articles, tables and benchmarks on the internet comparing WebSockets to SSEs, so I'll just briefly touch the obvious.
|Communication||Real-time and bi-directional||Transported over simple HTTP instead|
|Firewalls||Has troubles with corporate and outdated firewalls||No trouble with any firewall doing packet inspection|
|Latency||Lower because of persisted TCP connection||Can be higher sometimes because of multiplexing|
|Retries||Needs to be developed in user-land||Browsers have built-in retry mechanisms|
|Support||Browser native through ||Browser native through |
Join our newsletter
Want to hear from us when there's something new? Sign up and stay up to date!Recent issues of our newsletter
Building Open Source GraphQL Security
Learn how open-source boosts GraphQL security and explore defensive and offensive tools, resources, and best practices to protect your GraphQL APIs.
Open Source composition and validation library for Apollo Federation
Introducing MIT licensed drop-in replacement for the Apollo Federation composition library.
GraphQLConf 2023 Recap
Explore the highlights of GraphQLConf 2023. Learn about the latest trends in GraphQL, from gateway solutions to composite schemas and innovative projects.
State of GraphQL Gateways in 2023
A six-month journey of researching, benchmarking, exploring and comparing GraphQL gateways and the Federation spec in 2023.