DEV Community

Cover image for GraphQL over Internet
TheGuildBot for The Guild

Posted on • Edited on • Originally published at the-guild.dev

GraphQL over Internet

This article was published on Thursday, April 6, 2023 by Denis Badurina @ The Guild Blog

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.

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. No custom implementations for the browser Browser native through EventSource. But, can be custom implemented on fetch()

Presentation

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

Top comments (0)