GraphQL over Internet
Tags: graphql, http, ws, sse
Published: 2023-04-06 11:47:11

> This article was published on Thursday, April 6, 2023 by [Denis Badurina](https://twitter.com/enisdenjo) @ [The Guild Blog](https://the-guild.dev/)

## 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](https://github.com/graphql/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"](https://graphql.github.io/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](https://github.com/graphql/graphql-http)

To accompany the spec, the
[reference implementation `graphql-http`](https://github.com/graphql/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](https://graphql.github.io/graphql-over-http/). It does
so in the clearest and most explicit manner so that others implementing their own server can quite
literally use [`graphql-http`](https://github.com/graphql/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`](https://graphql-http.com)

Checking for compliance was never easier! [graphql-http.com](https://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](https://t.ly/IiwW)

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`](https://github.com/apollographql/subscriptions-transport-ws) library
that was built and maintained by [Apollo](https://www.apollographql.com/). It also had an
[accompanying protocol](https://t.ly/9CN_). 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](https://www.rfc-editor.org/rfc/rfc6455) 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](https://t.ly/b5N_) 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`](https://t.ly/L-GA).

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

### [Reference Implementation](https://github.com/enisdenjo/graphql-ws)

A specification goes best with a reference implementation. Say hello to
[`graphql-ws`](https://github.com/enisdenjo/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](https://t.ly/DnRO)

> 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](https://developer.mozilla.org/en-US/docs/Web/API/Event) + data inside
> the web page.

[*MDN on Server-sent events*](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)

### Why Not Just `EventSource`?

Before we proceed, you might ask why not just use the
[browser native EventSource](https://developer.mozilla.org/en-US/docs/Web/API/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](https://t.ly/DnRO)
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"](https://t.ly/DnRO#distinct-connections-mode) - basically the regular
    way you'd use SSEs, each request is a connection on its own
*   ["single connection mode"](https://t.ly/DnRO#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!

#### 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](https://graphql.github.io/graphql-over-http/), 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 `EventSource`s 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 `EventSource`s 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](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#listening_for_custom_events),
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](https://graphql.github.io/graphql-over-http/) with just one difference:
successful operation requests are responded with `202: Accepted` and the results are streamed to the
single established connection.

### [Reference Implementation](https://github.com/enisdenjo/graphql-sse)

Of course, a reference implementation goes along with the spec.
[`graphql-sse`](https://github.com/enisdenjo/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.

|               | WebSockets                                                                                                                                  | SSEs                                                                                                                                                                                                        |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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 [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket). No custom implementations for the browser | Browser native through [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). But, can be custom implemented on [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) |

## Presentation

Check out my presentation from [GraphQL Zurich Meetup](https://www.meetup.com/graphql-zurich/)
sponsored by [SwissLife](https://www.swisslife.ch/).

<Video src="https://www.youtube.com/embed/zgkx-ONm3Uc" title="GraphQL Zurich Meetup: GraphQL over Internet" />