Interacting with cached data
The ApolloClient
object provides the following methods for interacting
with cached data:
readQuery
readFragment
writeQuery
writeFragment
These methods are described in detail below.
Important: You should call these methods on your app's
ApolloClient
object, not directly on the cache. By doing so, theApolloClient
object broadcasts cache changes to your entire app, which enables automatic UI updates. If you call these methods directly on the cache instead, changes are not broadcast.
All code samples below assume that you have initialized an instance of ApolloClient
and that you have imported the gql
tag from graphql-tag
.
readQuery
The readQuery
method enables you to run GraphQL queries directly on your
cache.
If your cache contains all of the data necessary to fulfill a specified query,
readQuery
returns a data object in the shape of your query, just like a GraphQL
server does.
If your cache doesn't contain all of the data necessary to fulfill a specified
query, readQuery
throws an error. It never attempts to fetch data from a remote
server.
Pass readQuery
a GraphQL query string like so:
1const { todo } = client.readQuery({
2 query: gql`
3 query ReadTodo {
4 todo(id: 5) {
5 id
6 text
7 completed
8 }
9 }
10 `,
11});
You can provide GraphQL variables to readQuery
like so:
1const { todo } = client.readQuery({
2 query: gql`
3 query ReadTodo($id: Int!) {
4 todo(id: $id) {
5 id
6 text
7 completed
8 }
9 }
10 `,
11 variables: {
12 id: 5,
13 },
14});
Do not modify the return value of
readQuery
. The same object might be returned to multiple components. To update data in the cache, instead create a replacement object and pass it towriteQuery
.
readFragment
The readFragment
method enables you to read data from any normalized cache
object that was stored as part of any query result. Unlike readQuery
, calls to
readFragment
do not need to conform to the structure of one of your data graph's supported queries.
Here's an example:
1const todo = client.readFragment({
2 id: ..., // `id` is any id that could be returned by `dataIdFromObject`.
3 fragment: gql`
4 fragment myTodo on Todo {
5 id
6 text
7 completed
8 }
9 `,
10});
The first argument, id
, is the unique identifier
that was assigned to the object you want to read from the cache. This should match
the value that your dataIdFromObject
function assigned to the object when it was
stored.
For example, let's say you initialize ApolloClient
like so:
1const client = new ApolloClient({
2 ...,
3 cache: new InMemoryCache({
4 ...,
5 dataIdFromObject: object => object.id,
6 }),
7});
If a previously executed query cached a Todo
object with an id
of 5
, you can
read that object from your cache with the following readFragment
call:
1const todo = client.readFragment({
2 id: '5',
3 fragment: gql`
4 fragment myTodo on Todo {
5 id
6 text
7 completed
8 }
9 `,
10});
In the example above, if a Todo
object with an id
of 5
is not in the cache,
readFragment
returns null
. If the Todo
object is in the cache but it's
missing either a text
or completed
field, readFragment
throws an error.
writeQuery
and writeFragment
In addition to reading arbitrary data from the Apollo Client cache, you can
write arbitrary data to the cache with the writeQuery
and writeFragment
methods.
Any changes you make to cached data with
writeQuery
andwriteFragment
are not pushed to your GraphQL server. If you reload your environment, these changes will disappear.
These methods have the same signature as their read
counterparts, except they
require an additional data
variable.
For example, the following call to writeFragment
locally updates the completed
flag for a Todo
object with an id
of 5
:
1client.writeFragment({
2 id: '5',
3 fragment: gql`
4 fragment myTodo on Todo {
5 completed
6 }
7 `,
8 data: {
9 completed: true,
10 },
11});
All subscribers to the Apollo Client cache see this change and update your application's UI accordingly.
As another example, you can combine readQuery
and writeQuery
to add a new Todo
item to your cached to-do list:
1const query = gql`
2 query MyTodoAppQuery {
3 todos {
4 id
5 text
6 completed
7 }
8 }
9`;
10
11// Get the current to-do list
12const data = client.readQuery({ query });
13
14const myNewTodo = {
15 id: '6',
16 text: 'Start using Apollo Client.',
17 completed: false,
18 __typename: 'Todo',
19};
20
21// Write back to the to-do list and include the new item
22client.writeQuery({
23 query,
24 data: {
25 todos: [...data.todos, myNewTodo],
26 },
27});
Recipes
Here are some common situations where you would need to access the cache directly. If you're manipulating the cache in an interesting way and would like your example to be featured, please send in a pull request!
Bypassing the cache
Sometimes it makes sense to not use the cache for a specific operation. This can be done using the no-cache
fetchPolicy
. The no-cache
policy does not write to the cache with the response. This may be useful for sensitive data like passwords that you don’t want to keep in the cache.
Updating after a mutation
In some cases, just using dataIdFromObject
is not enough for your application UI to update correctly. For example, if you want to add something to a list of objects without refetching the entire list, or if there are some objects that to which you can't assign an object identifier, Apollo Client cannot update existing queries for you. Read on to learn about the other tools at your disposal.
refetchQueries
is the simplest way of updating the cache. With refetchQueries
you can specify one or more queries that you want to run after a mutation is completed in order to refetch the parts of the store that may have been affected by the mutation:
1mutate({
2 //... insert comment mutation
3 refetchQueries: [{
4 query: gql`
5 query UpdateCache($repoName: String!) {
6 entry(repoFullName: $repoName) {
7 id
8 comments {
9 postedBy {
10 login
11 html_url
12 }
13 createdAt
14 content
15 }
16 }
17 }
18 `,
19 variables: { repoName: 'apollographql/apollo-client' },
20 }],
21})
Please note that if you call refetchQueries
with an array of strings, then Apollo Client will look for any previously called queries that have the same names as the provided strings. It will then refetch those queries with their current variables.
A very common way of using refetchQueries
is to import queries defined for other components to make sure that those components will be updated:
1import RepoCommentsQuery from '../queries/RepoCommentsQuery';
2
3mutate({
4 //... insert comment mutation
5 refetchQueries: [{
6 query: RepoCommentsQuery,
7 variables: { repoFullName: 'apollographql/apollo-client' },
8 }],
9})
Using update
gives you full control over the cache, allowing you to make changes to your data model in response to a mutation in any way you like. update
is the recommended way of updating the cache after a query. It is explained in full here .
1import CommentAppQuery from '../queries/CommentAppQuery';
2
3const SUBMIT_COMMENT_MUTATION = gql`
4 mutation SubmitComment($repoFullName: String!, $commentContent: String!) {
5 submitComment(
6 repoFullName: $repoFullName
7 commentContent: $commentContent
8 ) {
9 postedBy {
10 login
11 html_url
12 }
13 createdAt
14 content
15 }
16 }
17`;
18
19const CommentsPageWithMutations = () => (
20 <Mutation mutation={SUBMIT_COMMENT_MUTATION}>
21 {mutate => {
22 <AddComment
23 submit={({ repoFullName, commentContent }) =>
24 mutate({
25 variables: { repoFullName, commentContent },
26 update: (store, { data: { submitComment } }) => {
27 // Read the data from our cache for this query.
28 const data = store.readQuery({ query: CommentAppQuery });
29 // Add our comment from the mutation to the end.
30 data.comments.push(submitComment);
31 // Write our data back to the cache.
32 store.writeQuery({ query: CommentAppQuery, data });
33 }
34 })
35 }
36 />;
37 }}
38 </Mutation>
39);
Incremental loading: fetchMore
fetchMore
can be used to update the result of a query based on the data returned by another query. Most often, it is used to handle infinite-scroll pagination or other situations where you are loading more data when you already have some.
In our GitHunt example, we have a paginated feed that displays a list of GitHub repositories. When we hit the "Load More" button, we don't want Apollo Client to throw away the repository information it has already loaded. Instead, it should just append the newly loaded repositories to the list that Apollo Client already has in the store. With this update, our UI component should re-render and show us all of the available repositories.
Let's see how to do that with the fetchMore
method on a query:
1const FEED_QUERY = gql`
2 query Feed($type: FeedType!, $offset: Int, $limit: Int) {
3 currentUser {
4 login
5 }
6 feed(type: $type, offset: $offset, limit: $limit) {
7 id
8 # ...
9 }
10 }
11`;
12
13const FeedWithData = ({ match }) => (
14 <