The GraphQL specification + best-practices: The quest for standardisation

Ifeora Okechukwu
7 min readDec 7, 2021

--

It’s no news that GraphQL is transport-agnostic. There are different type of transports available (on the web or any other platform similar to the web) to which GraphQL may/may not be applied with each transport type having its’ own protocol. These protocols are settled very nicely within the application layer of the OSI model. Some example of these transports include:

  1. SSH (Secure Shell) 🚫
  2. WS (Web Sockets) ✅
  3. HTTP (Hyper-Text Transfer) ✅
  4. SMTP (Simple Mail Transfer) 🚫

As you can see above, i have marked each transport type based on whether or not it would be feasible to place/operate GraphQL over them. SSH is canceled off because GraphQL (according to the specs) doesn’t blend well with the way SSH works or with what it was designed to do (such as generating SSH keys and creating encrypted channels and sending simple commands to a remote server). WS and HTTP are good to go because they are pull protocols (GraphQL is designed according to the specs around pull protocols) that supports data transfer in an elaborate and connection-oriented request/response context. Lastly, SMTP is canceled simply because it’s a push protocol so it’s not compatible with how GraphQL is designed.

P.S: This article doesn’t explain nor introduce GraphQL and/or its’ concepts. This article doesn’t teach you how to implement GraphQL for your project(s). This article also doesn’t force nor instruct you on which GraphQL best practices to opt into or out of. Lastly, this article doesn’t delve into GraphQL directives in detail. This article is for persons with sufficient knowledge about GraphQL and how it works and how to use it whom are now also looking for ways to do more with it and for it (GraphQL) by taking advantage of the specifications and the underlying transport mechanisms.

GraphQL has a lot of powerful ideas tucked neatly inside its’ specification. I like to think of specifications like guides (basic ideas/philosophies that you can build upon) and not as strict rules. I mean, REST was treated in a similar way not as an inflexible rule book of sorts as the authors had intended. Now, I think back to the days when REST was king on most web-based API servers and wish what would have been if REST wasn’t so damn strict. This is where i see an opportunity for GraphQL to grow, evolve and actualise!

The GraphQL specification is loose but places reasonable constraints and structure to what’s possible and this i believe is a good thing. I make bold to say that what will determine the path of growth and evolution that GraphQL makes in the future and what will make GraphQL much more powerful and useful than it is today is how the specification is implemented not just what is implemented from the specification.

In view of this, one problem i have always found with the current status quo as regards to GraphQL has been its’ inability to fully embrace the take full advantage of the capabilities of its’ host transport (e.g. HTTP or WS) until recently. There are GraphQL server libraries built solely over WS transport. There are again, of course GraphQL server libraries built solely over HTTP transport. But is GraphQL (and by extension the GraphQL specification) taking full advantage of the capabilities of the undelying transport to enhance itself for the demands of the web at scale ? I am not so sure about that.

The community and the spec working group(s) are however doing something about this but still need to do more. I have a few ideas that could get GraphQL to reaching its’ full potentials. Here we go:

There have been a lot of setbacks to scaling GraphQL for huge, enterprise and federated systems. Developers have come up with simple, robust yet ad-hoc practices to deal with these setbacks in the past by trying to sidestep what should originally not have been sidstepped. One thing REST did right in my opinion was embrace the protocol (HTTP) and all it’s goodies. This is exactly what GraphQL should do in order to evolve and meet the demands of the web at scale. Now, we are going to look at 2 things that GraphQL should have embraced a long time ago and needs to embrace more of going forward (using the HTTP transport as a case study):

HTTP Compression & HTTP streaming

In the GraphQL best-practices page, it is mentioned as “JSON (with GZIP)” in non-emphatic manner as something that may or may not be adopted. It isn’t part and parcel of the GraphQL specification and with good reason. Some GraphQL developers today will tell you that HTTP compression is not optimal or sufficient in the world of GraphQL as much as Deduplication is. Don’t get me wrong, deduplication works well but at scale it begins to lose its’ efficiency and impact and it no longer is an elegant solution. It also has the drawback of being applicable only when the entire GraphQL (HTTP) response is completely done and ready. So, the question is what happens when the GraphQL (HTTP)response takes a significantly long time to be ready ? The clients’ TTI (time-to-interactive) suffers and the server is burdened to have to respond with several of these slow-forming responses many a time per second. We also already know that query-splitting and pre-fetching isn’t going to cut it at scale. A better, cheaper and much more scalable solution will be to stream larger GraphQL JSON (HTTP) responses while dymaically compressing (gzipping) it using what the GraphQL @defer @stream directives which utilizes HTTP 1.1 or HTTP/2 streaming by setting up a chunked response using the HTTP header: Transfer-Encoding: chunked or similar to a suggestion here, we could build and utilise a @httpRequestHeader custom directive built with Apollo Server v2. Thankfully, these GraphQL directive(s) will soon become part of the GraphQL specification. This is good news but not enough. We also need a way to instruct the server to compress incrementally as it streams the response as opposed to a one-off manner using the same HTTP header but with some modification: Transfer-Encoding: gzip, chunked.

Perhaps, i suggest an inclusion into the GraphQL specification that has standardised, non transport-specific definition(s) which could be achieved with including one more payload key (e.g. “deliveryCompressed” : Boolean ) in addition to “query”, “operationName” and “variables” and/or a custom HTTP header (concepts that are common to the WS and HTTP transports) like X-Compressed-Delivery: incremental and X-Compressed-Delivery: one-off. For the @stream directive, we utilise the “incremental” value (which means that compressed data is sent to the GraphQL client incrementally using HTTP compression at each point while streaming the GraphQL response) and for the @defer directive, we utilise the “one-off” value (which means that compressed data is sent to the GraphQL client at once using HTTP compression at each point after deferring the GraphQL response) for the custom HTTP request header. Better yet, a directive be used to specify data response compression. For example:

An operation directive (client-side only)

HTTP Caching & HTTP Cache validation

It’s commonplace knowledge that HTTP POST requests aren’t HTTP cacheable and that they are the default method of requests on most GraphQL clients. Yes, i know that we now have strong and mature ad-hoc caching capabilities on the client-side and that browser HTTP caches have very little storage space compared to what the ad-hoc options present. Also, the HTTP cache on the browser is in my opinion better left for only static content (e.g. css, js, html). However, when GraphQL resolvers interact with REST, REST-like or gRPC endpoints on the server-side (either as part of a monolith or a micro-service), there should be proxy caches in the GraphQL server (positioned as an API gateway) as part of the gateway service that follow the HTTP spec and Cache-Control headers. Alternatively, we can ustise a third-party HTTP proxy cache like Cloudflare and strip the GraphQL API gateway from all caching duties but this might be expensive and redundant. I do believe that the ability to exploit the HTTP caching spec should be included as part of the GraphQL specifications in a standardized, non transprt-specific manner and not as part of the GraphQL best practices. It could be standardised and included as a directive in the GraphQL schema on the server-side (e.g. @httpCached(action: “one-off”, freshness: 1, validation: 0)” ). For this directive, the “freshness” setting for the directive can be used to check for how fresh or stale the cache is and the “validation” setting can be used to perform HTTP cache validation using either E-tag or Last-Modified HTTP response header. For example:

A schema directive (server-side only)

Furthermore, there is a case to be made for Persisted Queries which could make it possible for the HTTP cache on the browser to become usable again (as GET HTTP verb can now be used instead of POST) or better yet, have a CDN positioned between the browser and the GraphQL server like Apollo Server. Personally, i do believe that whichever method is chosen, the directive proposed above can work well.

In conclusion, i do believe these ideas i have expressed can be considered by the GraphQL-over-HTTP spec community as a way to standardise all the HTTP goodies into GraphQL where the goal is interoperability and integrability (say between an Apollo Client and a Relay Server). I have already created a PR to communicate these ideas and you can check them out. There are so many examples of how most of the directives that have been announced last year are being implemented in the real-world using the power of HTTP goodies (e.g. graphql-helix). It’s important to mention that as at this writing Apollo GraphQL has not implemented the @stream directive into the library while Relay GraphQL has implemented @stream and @defer directives.

It’s truly an exciting time for GraphQL and i can’t wait to see where it goes next.

--

--

Ifeora Okechukwu
Ifeora Okechukwu

Written by Ifeora Okechukwu

I like puzzles, mel-phleg, software engineer. Very involved in building useful web applications of now and the future.

No responses yet