Apollo Client Fetch Policy GraphQL React Typescript

May 6th, 2022 - written by Kimserey with .

Last week we looked at Apollo Client and how it could be used to manage queries to GraphQL server. In today’s post, we’ll look at the different fetching policies provided to manage query data and caching.

Apollo Client Cache

Apollo Client manages a cache for us behind the scene to optimise performance. The data queried are normalized and stored in memory when the same data our queried.

To visualise the cache, we can get the Apollo Client chrome extension and open the developer took to see the cache section.

picture

Fetch Policy

To modify the fetch policy, we can specify it in the options of useQuery:

1
2
3
const { loading, error, data } = useQuery<{
  books: { title: string; author: { id: string; name: string } }[];
}>(BOOKS, { variables: { bookId: id }, fetchPolicy: "cache-and-network" });

For example here we’ve specified cache-and-network. There are a number of options available to manage the cache:

  • cache-first hits the cache then if not present hit the server
  • cache-only only looks at the cache
  • network-only always consult the server but saves the result in cache
  • cache-and-network check in cache while also consulting server, and replace with server data if changed
  • no-cache consults the server and does not cache
  • standby same as cache-first except the query does not update the value

cache-first is the default behaviour, to use any other one, we must overwrite it.

Here is a working example to test the fetch policies.

We have a server with a query accepting a book ID.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import { ApolloServer, gql } from "apollo-server";

const typeDefs = gql`
  type Person {
    id: ID!
    name: String!
  }

  type Book {
    id: ID!
    title: String!
    author: Person!
  }

  type Query {
    books(id: ID): [Book]
  }
`;

const resolvers = {
  Query: {
    books: (_: any, args: any) => {
      console.log(new Date().toISOString(), args.id);
      return [
        {
          id: "1",
          title: "Made of Wolves",
          authorId: "1",
        },
        {
          id: "2",
          title: "The Visitor in the City",
          authorId: "2",
        },
      ].filter((b) => !args.id || b.id === args.id);
    },
  },
  Book: {
    author: (parent: any) => {
      return {
        id: parent.authorId,
        name: parent.authorId == "1" ? "James Carter" : "Arthur Novotic",
      };
    },
  },
};

async function main() {
  const server = new ApolloServer({ typeDefs, resolvers });
  await server.listen(4000);
  console.log("Server started on http://localhost:4000");
}

main();

And from our React app, we query the resolver:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const BOOKS = gql`
  query Query($bookId: ID) {
    books(id: $bookId) {
      title
      author {
        id
        name
      }
    }
  }
`;

const TestComp: FC = () => {
  const [id, setId] = useState<string>("");
  const { loading, error, data } = useQuery<{
    books: { title: string; author: { id: string; name: string } }[];
  }>(BOOKS, { variables: { bookId: id }, fetchPolicy: "cache-and-network" });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return (
    <>
      <button onClick={() => setId("")}>Get all books</button>
      <button onClick={() => setId("1")}>Get book 1</button>
      {data &&
        data.books.map((b) => {
          return (
            <div>
              {b.title} - {b.author.name}
            </div>
          );
        })}
    </>
  );
};

If we use the default cache-first, after hitting once Get all books and hitting Get book 1 buttons then back to Get all books, we can see that the backend is not consulted. If we change it to cache-and-network, we can see that the data comes from cache but the call is still made to the backend to update the data if it has changed.

And that conludes today’s post!

Conclusion

In today’s post, we looked at fetch policies in Apollo client queries. We started by looking at how we could explore our in memory cache with Apollo client extension, and then moved on to look at each fetch policies available. I hope you liked this post and I’ll see you on the next one!

External Sources

Designed, built and maintained by Kimserey Lam.