Released in mid-July, Apollo Client 3 offers a few new features, including package rearrange changes and more caching features. Let’s proceed to take a look at how to use the latest features from Apollo Client 3.
InMemoryCache
APIsThe InMemoryCache
API has expanded features. They include the eviction of objects and fields, garbage collection, types and fields configuration, and pagination helpers.
Let’s explore these changes by installing the @apollo/client
package with its dependencies by running:
npm i @apollo/client graphql react
We can add the InMemoryCache
into our Apollo Client by writing:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; const cache = new InMemoryCache(); const client = new ApolloClient({ uri: "https://graphqlzero.almansi.me/api", cache }); client .query({ query: gql` { user(id: 1) { id name } } ` }) .then(console.log);
The client
is created with the cache
option, which we set to the InMemoryCache
; the cached items will be in memory. Once we’ve done that, we can use the new InMemoryCache
features that come with Apollo Client 3.
We can evict the cached items by calling:
cache.evict();
We can optionally pass in the cached object’s ID by writing:
cache.evict({ id: 'user' })
We can also add a field
property of the object like so:
cache.evict({ id: 'user', fieldName: 'name' })
The cache.gc
method lets us do garbage collection on cached items. The object is determined to be reachable by tracing from the root to all the child references. Normalized objects that aren’t visited are removed.
To clear the unreachable cached items, we just call:
cache.gc();
Garbage collection can also be configured to retain some items. To retain the object with the ID 'user'
, for instance, we can write;
cache.retain({ id: 'user' })
We can configure how to deal with dangling references. When an object is evicted from the cache, it might have objects that have other cached objects. Apollo Client preserves these references because they may still be used later.
We can change how these references are dealt with by using a custom read
function. To do so, we would write:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; const cache = new InMemoryCache({ typePolicies: { Query: { fields: { ruler(existingRuler, { canRead, toReference }) { return canRead(existingRuler) ? existingRuler : toReference({ __typename: "user", name: "Apollo" }); } } }, user: { keyFields: ["name"], fields: { offspring(existingOffspring, { canRead }) { return existingOffspring ? existingOffspring.filter(canRead) : []; } } } } }); const client = new ApolloClient({ uri: "https://graphqlzero.almansi.me/api", cache }); client .query({ query: gql` { user(id: 1) { id name } } ` }) .then(console.log);
We set the ruler of the cache to our own ruler
function. We determine what references can be read.
If there’s an existing cache ruler, then we use it; otherwise, we get the item with toReference
. The offspring
method returns the objects where canRead
returns true
. This way, we know we can read those items.
We can create our own local field within the InMemoryCache
object.
For instance, we can write:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; const cache = new InMemoryCache({ typePolicies: { User: { fields: { age: { read(_, { variables }) { return Math.random() * 100; } } } } } });
We created a local field with name age
. This way, we can include the field in our query like the loading
state and the networkStatus
. variables
have the fields from the query. It also has the caching data.
It’s just a getter that returns a random number:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; const cache = new InMemoryCache({ typePolicies: { User: { fields: { age: { read(_, { variables }) { return Math.random() * 100; } } } } } }); const client = new ApolloClient({ uri: "https://graphqlzero.almansi.me/api", cache }); client .query({ query: gql` { user(id: 1) { id name age @client } } ` }) .then(console.log);
We get the age
field with age @client
. The @client
keyword distinguishes local fields from fields that are retrieved from the API.
Reactive variables is the new feature from Apollo Client 3.0 onwards. To create one, we use the makeVar
method from the @apollo/client
package. For instance, to create a children
reactive variable, we can write:
import { makeVar } from "@apollo/client"; const children = makeVar(["jane", "mary"]);
It returns a function that has the value of the reactive variable. To call it and get the value, we can write:
console.log(children());
The console log should read:
["jane", "mary"]
Reactive variables are useful for storing local state outside of the Apollo Client cache. This is different from local states and cached items, which are retrieved from the cache. Modifying a reactive variable automatically triggers an update of all active queries that depend on the variable.
We can also store local state with reactive variables. To do that, we can write:
import { ApolloClient, InMemoryCache, gql, makeVar } from "@apollo/client"; const age = makeVar(Math.random() * 100); const cache = new InMemoryCache({ typePolicies: { User: { fields: { age: { read(_, { variables }) { return age(); } } } } } }); const client = new ApolloClient({ uri: "https://graphqlzero.almansi.me/api", cache }); client .query({ query: gql` { user(id: 1) { id name age @client } } ` }) .then(console.log);
Above, we created the age
reactive variable, and we read it into the local state by returning it in the read
method. Then we can query age
like we do with other local states. Now, whenever our query changes, we’ll see a new value of age
returned as well.
To update the reactive variable, we just pass in a new value, like so:
import { makeVar } from "@apollo/client"; const age = makeVar(Math.random() * 100); console.log(age()); age(Math.random() * 100); console.log(age());
We pass in a new value to the function returned by makeVar
to update the value. Now both console logs should show different values.
We can define our own cache field policy so that we can read them in a way that’s different from what’s in the API.
For instance, we can write:
import { ApolloClient, InMemoryCache, gql } from "@apollo/client"; const cache = new InMemoryCache({ typePolicies: { User: { fields: { name: { read(name) { return name.toUpperCase(); } } } } } }); const client = new ApolloClient({ uri: "https://graphqlzero.almansi.me/api", cache }); client .query({ query: gql` { user(id: 1) { id name } } ` }) .then(console.log);
We created a type policy for the User
type. fields
has the fields we want to modify when reading, and we want the value of name
to be upper-case.
So we make the name
‘s read
method return the upper-case name. Now the console.log
call in the then
method should have the data
field with user.name
inside it being upper-case.
We can use this for many other applications, like setting default field values, transforming lists, changing field values, pagination, and much more.
Apollo Client 3 comes with many changes to caching, including the ability to clear cache data. We can also add local fields and change the ways normal fields are retrieved with cache policy.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.