If you’ve heard about GraphQL, you’ve likely also heard about GraphQL clients such as Apollo Client or Relay. GraphQL is an API technology, so you can always access your API with a simple HTTP fetch. These client implementations offer a large number of data management features, so their core purpose can become a bit muddled. In this short post, I’ll go through why sophisticated GraphQL client clients are useful, and what they will and won’t do for you.

The best way to understand the value of something is to lose it. So, let’s first imagine a world where GraphQL clients didn’t exist and then, from that point of view, figure out what a GraphQL client implementation would have to do to be useful.

If you haven’t tried GraphQL or Apollo Client yet, check out the interactive example on our website.

1. Sending correctly formed requests

Say you’re building a web application and you want to interact with a GraphQL backend. At the very basic level, all you have to do is construct POST requests with your query document, send them to the server and get a response back.

For example, you might send a JSON object like this:

{ query: "query { authors { firstName lastName } }" }

And receive something back from the server that looks like this:

{ data: { authors: [ { firstName: 'Alfred', lastName: 'Aho' }, { firstName: 'Ravi', lastName: 'Sethi' } ], } }

It would be nice to have something that would just construct this JSON object, send it down to the server, wait for a result and error in a reasonable way in case something goes wrong. In addition, you’ll want this process of constructing the HTTP request and handling corresponding response to be pluggable with middlewares and afterwares, so that you can do authentication by adding an HTTP header.

Once you start adding additional features to your GraphQL implementation, like subscriptions, a simple HTTP fetch is no longer sufficient. In this case, a great network interface implementation can really help you out.

That’s what Network Interfaces do in Apollo Client. You could use these independently of the rest of the implementation and still have something useful, and we’re hoping to make that component more independent in the future.

2. Keeping your UI consistent across queries

You don’t just want to fetch data; you also want to show this data to your users through a UI component. Let’s say that you have two components within your UI that each display a list of authors. So, you have two components that ask for this query to be sent to the server:

query { authors { firstName lastName } }

Let’s call these two components UI Component #1 and UI Component #2.

The initial UI after we’ve loaded our authors’ names.

Let’s say the user clicks the “reload list” button. List #1 has to reload its list of authors’ names and display the new ones to the user. The result it now gets back looks like this:

{ data: { authors: [ { firstName: 'Alfred', lastName: 'Aho' }, { firstName: 'Ravi', lastName: 'Sethi' } , { firstName: 'Jeffrey', lastName: 'Ullman' }, ], } }

So, #1’s view will update to include this new author by the name of “Jeffrey Ullman.” But, “#2” will still be stuck in the past.

Disaster strikes when we hit “reload” on the first author list

We don’t want this to happen, because the application user might get confused about the state of the server. Is that author in there or not? Ideally, both “#1” and “#2” would update with the latest data and our UI wouldn’t fall out of sync. This is what Apollo Client’s “reactive cache” accomplishes.

The reactive cache is a collection of GraphQL query results held on the client that’s updated as the client fetches more information. We sometimes call it a “reactive” cache since it allows us to make sure that when a single piece of data is updated, every query watching that piece of data is updated as well. When you fire queries and mutations through Apollo Client, the results are written to the cache and it is used to make sure that all of the “active” queries on your page are up-to-date with each other. There are some more details as to how exactly the reactive cache works but the core idea is that keeps the different parts of your UI in sync.

Happy days are here again when a GraphQL client preserves consistency

To learn more about how the cache works in Apollo, fire up an example app and install the Apollo Client Devtools. You’ll see how the query results are decomposed into a normalized structure that allows objects to be shared across queries.

3. Updating the cache

Of course, in order for the reactive cache to work, we need to be able to update it with information returned from the server. In most cases, this is as easy as fetching a GraphQL query or running a mutation. When the result from the query is returned, your GraphQL client will automatically update the cache entries associated with the query. For mutations as well, if the results match up with current cache entries, they are incorporated automatically.

But, in cases where the data returned is something that includes completely new objects, for example pagination and mutation that create new items, more information is needed to specify how exactly the cache should be updated. So, we want our GraphQL client to give us a way to update the cache with new information from the server and allow us to handle these special cases. For Apollo Client, we’ve documented these cases and how to deal with them.

Summary

That gives us the three core responsibilities of a GraphQL client:

Allow the application developer to easily execute GraphQL queries, and configure transport-specific features like headers. Ensure that all GraphQL results currently being displayed in an app are consistent with one another. Provide flexible ways to update the cache with results from the server when using mutations, pagination, subscriptions, and more.

The structure inherent in GraphQL makes it possible for libraries like Apollo Client to implement the above properties to make your development experience more pleasant. In particular, the most useful property of GraphQL that enables this is that GraphQL can be used both to request the data from the server and to read data from the cache for the UI components.

Common misconceptions

We now know that GraphQL clients are fundamentally responsible for these three requirements. Let’s dig into the details a bit and clear up some existing misconceptions around the role of client implementations.

Performance is the main reason to use a GraphQL client. These GraphQL clients are often referred to as “caches”. Caches are generally used to speed things up. So, many assume that the primary benefit of Apollo Client or Relay comes from the fact that it is able to improve the performance of your application by lowering roundtrips. It is certainly the case that this may occur: if you’re fetching data you have already requested before, Apollo Client will happily resolve it from the cache for you and save you a server roundtrip. But, this is not the primary goal of the reactive cache. The cache is meant to keep your UI consistent and predictable, and most implementations will prioritize doing that over saving your application a roundtrip to the server. Consistency between client and server. Apollo Client’s cache is currently concerned with consistency at the UI level, not at the client-server level. This means that the client will not automatically receive updates from the server in order to update your UI components. Live updates can be implemented with polling or GraphQL subscriptions. These aren’t too hard to set up, but they require the developer to opt in and do some extra work.

Spherical Cow

Physicists sometimes use a term for a description of something that’s simplified to the point where it might only capture a very vague view of a reality: a spherical cow. In this post, I’ve essentially presented such a model of Apollo Client and other GraphQL client implementations. There’s a lot of stuff it does under the hood to make your GraphQL app consistent, performant and pleasant to develop that I’ve just ignored.

But, the core responsibilities described make it clear what Apollo Client’s role is: fetch some GraphQL queries and keep them in sync with one another. Hopefully this simple mental model makes it a bit easier to navigate the maze of tools, libraries, etc. exist and get you to productivity more quickly.

If you want to learn more about concepts like this, and hear from both production GraphQL users and contributors from the Apollo community, please sign up for our free 1-day event on April 12, Apollo Day: Apollo Day: GraphQL Developer EventJoin us for a special developer event to celebrate the Apollo project’s first anniversary and the official launch of…www.eventbrite.com