This tweet by Phil Sturgeon a week or so ago stirred the pot of a lot of GraphQL enthusiasts.

Around the same time, this project called Vulcain was announced. (Which by the way looks great!) Part of the announcement included “TL/DR: you don’t need #GraphQL anymore!”.Finally, this week, Mark Nottingham posted an amazing article on the power of HTTP/2 and what it means for API design.

All  of these did make me think about this subject some more. When  everything in this world starts running HTTP/2 (and HTTP/3), do we all  have no more reasons to use GraphQL? Let’s dive into it a little bit.

OK,  so let’s start by understanding what HTTP/2 brings to the table that  would change the value of GraphQL. There are a ton of new things in  HTTP/2 like a new binary format and better header compression, but the  thing that makes the most sense for us to talk about in the context of  GraphQL is the way HTTP/2 handles delivery of requests/responses.

Opening  TCP connections is a costly operation that a lot of HTTP/1 clients want  to avoid. For this reason, developers have often tried to  limit the amount of requests because of their large overhead, with  things like batching, query languages, inlining css/js, sprites, etc.  HTTP/1.1 attempted to solve some of these issues with persistent  connections and pipelining.  These two things allowed browsers to send multiple requests and  responses over the same connection. The problem is this was quite  vulnerable to head-of-line blocking, meaning one slow request could slow down every other behind it. The smart people behind HTTP/2 solved this issue in a different way:  Along with this new binary protocol comes a new delivery strategy. HTTP/2  opens a single connection, but multiplexes requests and responses using  a new binary framing layer, where each frame is part of a stream. Clients and servers are then able to reconstruct requests and responses stream numbers present on each frame. This allows HTTP/2 to very efficiently handle a lot of requests over a single connection.

Not only that, but HTTP/2 has a new concept called Server Push. Without going into too much detail, server push allows servers to preemptively send responses before the client requests them. The best example for this are stylesheets and javascript assets. While  responding to an HTTP request, a server could detect an HTML page it is  rendering contains a stylesheet and anticipate the following request  for the stylesheet, already sending the response before the client asks  for it. This is what Vulcain, the project we talked about at the beginning of the article uses to efficiently fetch linked resources.

OK, cool, but what does this even have to do with GraphQL? Let’s dive into it.

GraphQL: A Single Request to Rule Them All

Part  of the appeal for GraphQL is that it helps us deal with those expensive  HTTP/1 connections better. That’s because GraphQL lets client select  every single possibility exposed by the server within a single round  trip. Compare this with a hypermedia focused API, which usually requires  a lot of network requests (Something caching can help with).

https://graphql.org/ uses this as a “selling” point.

Most  people who are declaring GraphQL useless with HTTP/2 are specifically  talking about this point. Batch APIs, query languages like GraphQL,  embedded relations, and even bigger custom endpoints become way less  appealing in that sense if the cost of making requests become small, and  that’s totally true! But is that the only reason we use GraphQL? I  don’t think so.

Gotcha: It’s still early days for both HTTP/2 clients and certain application servers

This  one is not a good excuse but it is worth mentioning. Using HTTP/2 at  the application layer is far from a solved problem in some ecosystems.  I’ll let you google for Rack/Rails over HTTP/2, it’s quite something.  This is because a lot of application servers have been built with the  request/response pattern, and starting to think in terms of those HTTP/2  streams is not an easy task, especially for certain frameworks.  But, it’s not a good excuse, a lot of ecosystems do support it well,  and in theory, we should still aim to make this better! (Most proxy servers do support it though, but it’s hard to do things like server  pushes if the app server is stuck in that req/response pattern.)

GraphQL is not only about reducing round trips or about over/under-fetching

While  we see these two things being advertised a ton, GraphQL gives us a lot  more than the ability to reduce round trips, or to reduce the number of  bytes transmitted over the network.

GraphQL’s power, and where it’s making the most tradeoffs is how client-centric it is. Over the past few years this has been a concern in the minds of a lot  of people. Daniel Jacobson wrote a bunch of amazing articles 5–7 years  ago about some of these issues. Here’s another one.

Our REST API, while very capable of handling the requests from our devices in a generic way, is optimized for none of them.

Notice that this isn’t necessarily about REST needing us to make more requests, or transmitting unnecessary  data. It is more about designing an API that enables support for  multiple and different client use cases. A common way to handle this  problem is to let client logic be ran closer to the server, like Netflix’s client adapters mentionned in that 2012 post. Since then, some teams there have even been using GraphQL. The BFF pattern is another abstraction that aims to solve similar issues.

GraphQL  redefines that client server boundary by helping us build a server  engine capable of compiling client use cases into server resources. Persisted queries make this even easier to see, being essentially client generated server resources.

GraphQL  as a server side abstraction is something to keep in mind when  discussing whether it’s still relevant in an HTTP/2 world. While  maintaining a variety of use cases can cause issues in typical endpoint based APIs, GraphQL  enables API providers to focus on exposing many possibilities without  needing to think about the cost on existing clients and without the added complexity of maintaining tons of different resources. (It does come with its costs: hard to optimize performance, not always very cacheable. The same tradeoffs highly customizable APIs often come with)

The Client Developer Experience

I  tend to focus a lot on server side stuff in these posts, but it’s  important to remind ourselves that GraphQL is especially loved at the  client level! GraphQL fragments are an absolutely awesome abstraction  when paired with the component pattern we see in modern frontend  frameworks. Again, paired with persisted queries, the GraphQL client  developer experience can be quite incredible.

It’s the Whole Package that Makes it Great

GraphQL  isn’t inherently special, and yes alternatives will exist! Typed  schema? Just use OpenAPI! Server-side abstraction to handle multiple  client use cases? There are many ways to do that. Introspection?  Hypermedia can allow clients to discover actions and can start from the  root too. The amazing GraphiQL? I’m sure there’s something for OpenAPI.  It’s always possible to recreate parts of what GraphQL has. However,  it’s everything together, found under one specification that made  GraphQL appealing and great to so many of us. I suspect that’s also what  made it grow so fast also. Because there’s so much guidance into  building this API style, language specific libraries for GraphQL tend to  be high quality and well adopted.

The Network is Still (will always be?) A Constraint

Here’s a last thought. No matter how fast network requests will be, it seems like they will always remain some sort of constraint. This is why we don’t design web APIs like typical programming language objects for example:

Snippet from Patterns of Enterprise Application Architecture: https://martinfowler.com/books/eaa.html

While HTTP/2 definitely favours finer grained requests, I think the tradeoff still exists.

Can HTTP/2 help GraphQL?

OK  so GraphQL brings us tons of other important things, but HTTP/2 is  still really awesome. Something to possibly look at in the future is if  we can bring some of this power to GraphQL too. Something like this:

query {
  viewer {
    name
    posts(first: 100) @stream {
      title
    }
  }
}

Where  we can still use GraphQL’s server side abstraction, it’s declarative  query language, while using HTTP/2 streaming. This is something that’s  been explored at Facebook, using web sockets I believe. I need to look  into this more, but I know people are already exploring directives such as @defer, @stream and @live.

Final Words

HTTP/2 is awesome, and the example in this article in particular is quite mind blowing! And if GraphQL is simply a way to reduce the number of round trips  you’re making, or about saving bytes, chances are you’ll be happy with  an HTTP/2 powered endpoint-based API! However If you’ve also felt the  other powers we’ve discussed in this article, you probably also realize  that GraphQL brings us much more than this.

Thanks for reading 💜

Marc

Enjoyed the post? Subscribe to Production Ready GraphQL!