Let’s Talk About Executing and Caching Queries with React Apollo
Tags: graphql, react, javascript
Published: 2021-10-27 16:45:31

![Cover photo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13ynlmycoph96dp9tem4.jpg)

Yep, you’ve guessed it. We’re going to talk about queries. 

Let’s start with basics. The `useQuery` React hook is the primary API for executing queries when using Apollo Client in React. To run a query within a React component, we call the `useQuery`  and pass it a GraphQL query string. When the component renders, `useQuery`  returns an object from Apollo Client that contains `loading`, `error`, and `data` properties. 

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=query-hook.ts %}

Apollo Query component is a deprecated way of executing queries. It’s a part of `@apollo/react-components` package and it is implementing Render props pattern. Render props are used for sharing code between React components using a prop whose value is a function. A component with a render prop takes a function that returns a React element. The component then calls this function instead of implementing its own render logic. In the case of Query component, we are using the **children** prop as a render prop. But because the children prop doesn’t need to be named in the list of “attributes”,  you can put it directly inside the element. In the following example, we are passing a query prop, which is required. 

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=query-component.ts %}

Internally, Query component actually uses the `useQuery` hook and then calls the render props function with results returned from the hook. This means that we’re using the same options and are getting the same result object as when using the `useQuery` hook. 

### Cache-first, Ask Questions Later

Apollo is locally caching results for the queries by default. This makes subsequent executions of the same query extremely fast. This is called **cache-first** policy. We can define fetch policy on query level.  

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=fetch-policy.ts %}

These are 6 supported fetch policies: 

- **cache-first**: This is the default fetch policy. If data is present in the cache, that data is returned. Otherwise, a query is executed against the GraphQL server and the data is returned after it is cached. 

- **cache-only**: Query is only executed against the cache. GraphQL server is never called. 

- **cache-and-network**: If data is present in the cache, that data is returned. But even then, a query is executed against the GraphQL server. If the response differs from what is stored in the cache, it will update the cache. 

- **network-only**:  Query is executed against the GraphQL server, without first checking the cache. The query's result is stored in the cache in case the same query with a different fetch policy is being made elsewhere in the application. 

- **no-cache**: Behaves like network-only, except the query's result is not stored in the cache. 

- **standby**: Behaves like cache-first, except this query does not automatically update when underlying field values change. You have to update it manually. 

There is also a possibility to define **nextFetchPolicy** option. This way, you can define fetch policy for the first query execution using **fetchPolicy** and then you can define fetch policy for the subsequent executions with **nextFetchPolicy**. 

### Apollo Client Behind the Scenes

Apollo Client acts as a facade to the underlying cached data. Behind the scenes, Apollo normalizes the data by splitting the results into individual objects and assigns a unique identifier to each object. These objects are stored in a flattened structure. Apollo then exposes an API to interact with the cached data. By minimizing direct access to the actual data using the facade/API, Apollo can normalize data under the hood. 

Apollo can **automatically update cache** for: 
- Queries 
- Mutations that update a single existing entity 
- Bulk update mutations that return the entire set of changed items 

Of course, there are use cases in which we have to **manually update the cache**: 
- Application-specific side-effects (something that happens after the mutation, but does not use data returned from the mutation) 
- Update operations that add, remove, or reorder items in a cached collection 

### Manually Updating Cached Data

Apollo supports multiple ways of reading and writing cached data:
- `readQuery / writeQuery`
- `readFragment / writeFragment`
- `cache.modify`

With `readQuery` method, it is possible to run GraphQL queries directly on the local cache. If the cache contains all of the data necessary to execute a specified query, `readQuery` returns a data object in the shape of the query, just like a GraphQL server does.  If some fields are missing from the cache, `null` is returned. Using `writeQuery` we can write arbitrary data to the cache for the specific query. It looks similar to `readQuery`, but it accepts `data` option. 

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=read-query.ts %}

Using fragments it is possible to read or update only parts of the cached data, unlike `readQuery / writeQuery` methods, which require a complete query. When using fragments to interact with cache, we can use `readFragment / writeFragment` methods. They require `id` option, which represents the unique identifier that was assigned to the object in the cache.  By default, this identifier has the format `<_typename>:<id>`, but this can be customized. If there is no object with the specified ID, `readFragment` returns `null`.  `writeFramgent` accepts `data` option, which represents data that will be written for the specified object and that conforms to the specified fragment. 

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=read-fragment.ts %}

With `cache.modify` it is possible to directly modify cached fields. This method requires the ID of the cached object to modify and a modifier function for each field to modify. 

It is important to emphasize that any changes made with these methods are not pushed to the GraphQL server. If the current page is refreshed, these changes will disappear. Also, all methods trigger a refresh of all active queries that depend on modified fields. 

### Two Strategies for Updating the Cached Results 

Besides manually rewriting cached data, there are two strategies for updating the cached results: **polling** and **refetching**. 

With polling, we execute the query periodically at a specified interval. 

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=poll-interval.ts %}

Refetching is done by using `refetch` function that enables us to re-execute the query.

{% gist https://gist.github.com/sdanko/3749048756da7651056cd1087c05daa1 file=refetch.ts %}

### Apollo Client is a Powerful Caching Machine

In conclusion, one would have to agree that Apollo Client is a mighty caching machine. It’s equipped with a powerful caching mechanism that makes it  easy to execute data queries quickly and efficiently. However, to make better use of its caching possibilities, one should get familiar with various methods of interacting with cache, cache setup and configuration.