Data Query Patterns for Apollo GraphQL client

Apollo GraphQL Client (JavaScript) offers extensive options for building efficient data queries using the cache, Optimistic UI, and error handling. Developers can use various ways to fetch data, store it in the local cache or handle different types of errors.

With so many different ways to query data developers can often write suboptimal queries or rely on defaults that may give different results than expected. Apollo GraphQL documentation provides very comprehensive coverage for network and cache updates, however, they are documented in many places. To get full insights developers need to jump between different pages of documentation and sometimes even GitHub issues.

In this blog post, we going to summarize most of the fetch options that Apollo GraphQL offers and map them to patterns that can be employed by developers in their applications.

Note: Examples in this blog post using plain Apollo JavaScript Client, however, patterns and options can be used in any framework-specific implementations like React, Angular and Native Clients. This post will not cover client-side data storage and direct cache access.

What is the cache and why we need it?

One of the prerequisites for better query efficiency is to use Apollo Normalized Cache. The Cache will give us the ability to reduce the number of queries that are sent to the server and reuse already existing data. The cache layer is optional and it can be configured when creating Apollo Client:

The cache layer will be kept in memory and removed on the website/application restart. To keep cache across restarts, developers can use a separate package —

Apollo client offers a and methods used to execute GraphQL operations. Additionally to query and variables properties, both methods come with very important flags that can be used to interact with the network and cache layer.

Image for post
Image for post

In the next section, we going to explain the meaning of flags and how they interact with each other.

Caching GraphQL Queries

For the client query method, we can supply the number of properties. Most important ones are:

  • fetchPolicy — How you want your component to interact with the Apollo cache.
  • errorPolicy — How you want your component to handle network and GraphQL errors. Users

Demystifying fetch-policy

Fetch policy accepts the following options:

  • cache-first: When using this policy Apollo will make network connection only for the first time and utilize cache for the next requests. When data on the server changes it will be not reflected in the cache. This option is perfect when fetching large amounts of data that changes rarely. For example, when making a query to fetch UK Post Zip-Codes codes will be fetched only once and then they will be available for the entire lifecycle of the application. This option will be enabled by default and in most cases, developers will need to change it as it will prevent from fetching fresh data from the backend.
Image for post
Image for post
  • cache-and-network: In this policy, we are getting data fast from cache (if exist) and then supplement it with the response from the server.
    If all the data needed to fulfill query is in the cache then that data will be returned. However, regardless of whether or not the full data is in your cache, this fetchPolicy will execute the query with the network This fetch policy optimizes for users getting a quick response while also trying to keep cached data consistent with your server data at the cost of extra network requests. This policy will be best if you looking for simplicity for most of the general cases and can afford to reach the server with every query.
Image for post
Image for post
  • network-only: This fetch policy will always make the request to the server and update the cache. Using this fetch policy will keep the cache up to date and always deliver fresh results from the server. The cache will be never read by this query itself but it can be used in other queries. Users will see results only when the network request finishes.
Image for post
Image for post
  • cache-only: This fetch policy will execute a query using your network interface. Instead, it will always try reading from the cache. If the data for your query does not exist in the cache then an error will be thrown. This fetch policy allows you to only interact with data in your local client cache without making any network requests which keeps your component fast, but means your local data might not be consistent with what is on the server.
Image for post
Image for post

At this point, we should know all the options that will help us to manipulate our cache.

Image for post
Image for post

Cache Patterns

Working with the cache is hard

https://twitter.com/codinghorror/status/506010

When implementing a cache for the first time developers often need to change their mindset and design application assuming that cache will be used. The misconfigured cache can give users old data and lead to query inefficiency. Hopefully, we can use proven patterns to make sure that our data is always up to date.

  1. REST-like experience: Online NetworkOnly

TL;DR

IF(ONLINE) 
Query Network-Only
ELSE
Query Cache-Only

This pattern provides a very simple and convenient way for GraphQL caching that will always give developers the latest data. Clients will always make requests to the server if there is a network connect. If requests will fail or if there is no network connection developers can always fallback to the cache that will be up to date thanks to network only strategy.

This strategy is recommended for the applications that want to achieve the following targets:

  • Always up to date data no matter what is in the cache
  • Provide seamless offline experience from cache
  • Avoid advanced topis like subscriptions, cache invalidation etc.

The obvious drawback of this approach is an intensive chattiness of the client-server communication and no ability to see the results faster (although OptimisticUI can still be used)

2. Spare the server! Subscription-based cache Refill

TL;DR

On Start: Query Network-Only
Later: Subscribe DATA
Query Cache-Only

In cases where developers want to reduce the number of queries to the server and utilize subscriptions, a CacheRefill strategy can be used. This strategy focused on using the cache layer extensively as the main source of the data. Then developers can configure an alternative cache refill strategy:

  • Pooling
  • Subscriptions

Despite using subscriptions clients will still go offline. This strategy works with applications like chat where in most of the cases local cache can be used to deliver a seamless experience. However, subscriptions would be working only for some specific amount of time (chat view opened etc.) so network-only queries are still needed.

3. Master-detail — Reuse Cache

When working with the master-detail view we can always prefetch data on master view and reuse cached data in the detail view — eventually prefetch extra data that is needed for details. This pattern is considered a simple query optimization.

4. Optimistic fetch

Optimistic fetch is a variation of the Online Network-Only strategy

TL;DR

Query Cache-Only
IF(ONLINE)
Query Network-Only

Basically this means your UI’s queries will always work if the requested data is available in the local cache and it will always keep the cached data consistent with your server data if it can be reached.

5. Direct cache manipulation

Apollo client allows to directly interact with the cache to use it as a database. For most use cases this is not needed thanks to refetchQueries feature that will help to update queries that could be affected. RefetchQueries is a very verbose way to work with that can degrade client performance.

That is why Apollo and other frameworks provide cache update functions where developers want to have direct access to the cache or manipulate optimistic UI objects.

NOTE: Apollo 3.0 which is currently beta is going to provide another set of useful helpers for better cache manipulation.

Image for post
Image for post
Apollo Client 3.0 Features

Ocean of Boilerplate

Once we learn about all options and practices is very easy to see a repeating pattern across the code. Most of the update functions will look almost the same. Optimistic responses will be almost always build using data that was provided in input etc.

Image for post
Image for post

If you looking for a very quick way to adopt the cache strategies feel free to take look into offix-cache package that provides helpers for common cache updates and data deduplication for Apollo Clients.

https://offix.dev/docs/offix-cache

Reference blog posts

Writing about Teaching, OpenSource, Mobile and Cloud. https://wtrocki.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store