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 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.
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 servercache-only
only looks at the cachenetwork-only
always consult the server but saves the result in cachecache-and-network
check in cache while also consulting server, and replace with server data if changedno-cache
consults the server and does not cachestandby
same as cache-first except the query does not update the valuecache-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!
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!