Configuring Cache Backends

How to configure Apollo Server's cache


Many Apollo Server features take advantage of a cache backend (these features include automatic persisted queries , the response cache plugin , and RESTDataSource ). Apollo Server uses an in-memory cache by default, but you can configure it to use a different backend, such as Redis or Memcached.

You can specify a cache backend by passing a cache option to the ApolloServer constructor. Your specified cache backend must implement the KeyValueCache interface from the @apollo/utils.keyvaluecache package.

There are many cache backend implementations to choose from, including several implementations provided by Apollo. For example, Apollo maintains an implementation of InMemoryLRUCache in the @apollo/utils.keyvaluecache package. Apollo also provides a wrapper class for the keyv package (which implements several cache backends) named KeyvAdapter in the @apollo/utils.keyvadapter package.

Configuring in-memory caching

⚠️ If you are using Apollo Server 3, see the previous version of this article to learn how to protect your cache from denial of service attacks by using a bounded cache. By default, Apollo Server 4's default cache is a bounded in-memory cache backend.

You can configure your server to use a different backend (such as Redis or Memcached) using the cache constructor option.

If you want to configure the default in-memory cache, Apollo provides the InMemoryLRUCache class from the @apollo/utils.keyvaluecache package.

The InMemoryLRUCache class is a wrapper around the lru-cache package and has a default maximum of approximately 30MiB of memory. You can configure an instance of InMemoryLRUCache with the same options as found in the lru-cache package, see the lru-cache documentation for more details. You can use Apollo's InMemoryLRUCache by passing it to the cache option of the ApolloServer constructor like so:

TypeScript
1import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache';
2
3const server = new ApolloServer({
4  cache: new InMemoryLRUCache(),
5});

Apollo Server 4 provides the equivalent of the above code snippet out-of-the-box and doesn't require you to install the @apollo/utils.keyvaluecache package.

In this example, we've increased the default size and provided a default TTL. For more information on these configuration options, see the lru-cache documentation .

TypeScript
1import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache';
2
3const server = new ApolloServer({
4  // ...
5  cache: new InMemoryLRUCache({
6    // ~100MiB
7    maxSize: Math.pow(2, 20) * 100,
8    // 5 minutes (in seconds)
9    ttl: 300,
10  }),
11});

Configuring external caching

Apollo no longer maintains any caching backends directly. Instead, we recommend using the keyv package along with the KeyvAdapter class provided by the @apollo/utils.keyvadapter package.

KeyvAdapter wraps a Keyv instance and implements the KeyValueCache interface which is required by Apollo Server. You can use the KeyvAdapter class to wrap a Keyv instance and provide it to the cache option of the ApolloServer constructor like so:

  1. Install the required packages

Bash
1npm install keyv @apollo/utils.keyvadapter
  1. Configure the Apollo Server cache

TypeScript
1import Keyv from 'keyv';
2import { KeyvAdapter } from '@apollo/utils.keyvadapter';
3
4const server = new ApolloServer({
5  // ...,
6  cache: new KeyvAdapter(new Keyv()), // highlight-line
7});

Implementing your own cache backend

If your requirements are specialized or you'd prefer to implement your own cache backend, you can implement the KeyValueCache interface and pass it to the ApolloServer constructor directly.

The KeyValueCache interface is shown below:

TypeScript
1interface KeyValueCache<V> {
2  get(key: string): Promise<V | undefined>;
3  // ttl is specified in seconds
4  set(key: string, value: V, options?: { ttl?: number | null }): Promise<void>;
5  delete(key: string): Promise<boolean | void>;
6}

Configuring Redis

The @keyv/redis package uses the ioredis package under the hood. The second options argument is passed through to the ioredis.Redis constructor. See the ioredis docs for a list of available options.

Start by installing the required packages:

Text
1npm install keyv @keyv/redis @apollo/utils.keyvadapter

Single instance

TypeScript
1import Keyv from "keyv";
2import { KeyvAdapter } from "@apollo/utils.keyvadapter";
3
4const server = new ApolloServer({
5  typeDefs,
6  resolvers,
7  cache: new KeyvAdapter(new Keyv("redis://user:pass@localhost:6379")), // highlight-line
8});

Redis Sentinel

TypeScript
1import Keyv from "keyv";
2import { KeyvAdapter } from "@apollo/utils.keyvadapter";
3
4const server = new ApolloServer({
5  typeDefs,
6  resolvers,
7  // highlight-start
8  cache: new KeyvAdapter(
9    new Keyv("redis://user:pass@localhost:6379", {
10      sentinels: [
11        { host: "localhost", port: 26379 },
12        { host: "localhost", port: 26380 },
13      ],
14    })
15  ),
16  // highlight-end
17});

Redis Cluster

The @keyv/redis package doesn't support ioredis.Cluster out of the box. Instead, we can create our own ioredis.Cluster instance and pass that to keyv as the store object. See the ioredis.Cluster docs for a list of available options.

Start by installing the packages we'll need:

Text
1npm install keyv @keyv/redis ioredis @apollo/utils.keyvadapter
TypeScript
1import Keyv from "keyv";
2import KeyvRedis from "@keyv/redis";
3import Redis from "ioredis";
4import { KeyvAdapter } from "@apollo/utils.keyvadapter";
5
6// highlight-start
7const cluster = new Redis.Cluster([
8  { host: "localhost", port: 26379 },
9  { host: "localhost", port: 26380 },
10]);
11// highlight-end
12
13const server = new ApolloServer({
14  typeDefs,
15  resolvers,
16  // highlight-start
17  cache: new KeyvAdapter(new Keyv({ store: new KeyvRedis(cluster) }), {
18    disableBatchReads: true,
19  }),
20  // highlight-end
21});

Note the disableBatchReads option. This disables batching which isn't supported by ioredis.Cluster.

Configuring Memcache

The @keyv/memcache package uses the memjs package under the hood. Its second options argument is passed to memjs.Client.create(). See the memjs docs for a list of available options.

Start by installing the required packages:

Text
1npm install keyv @keyv/memcache @apollo/utils.keyvadapter
TypeScript
1import Keyv from "keyv";
2import KeyvMemcache from "@keyv/memcache";
3import { KeyvAdapter } from "@apollo/utils.keyvadapter";
4
5// servers is a comma-separated list of strings
6// highlight-start
7const servers = [
8  "user:pass@localhost:11211",
9  "user:pass@localhost:11222"
10].join(",");
11
12const memcache = new KeyvMemcache(servers, {
13  retries: 10,
14  expires: 60,
15});
16// highlight-end
17
18const server = new ApolloServer({
19  typeDefs,
20  resolvers,
21  cache: new KeyvAdapter(new Keyv({ store: memcache })), // highlight-line
22});

Handling cache fetching errors

To provide error tolerance for cache backends that connect via a client (e.g., Redis), we recommend using the ErrorsAreMissesCache wrapper from @apollo/utils.keyvaluecache . If the cache is unavailable and your request throws an error, ErrorsAreMissesCache treats that error as a cache miss. At the same time, your cache client can keep trying to reconnect to your cache backend until things are working again, like so:

TypeScript
1import Keyv from "keyv";
2import { KeyvAdapter } from "@apollo/utils.keyvadapter";
3import { ErrorsAreMissesCache } from "@apollo/utils.keyvaluecache";
4
5const redisCache = new Keyv("redis://user:pass@localhost:6379");
6const faultTolerantCache = new ErrorsAreMissesCache(
7  new KeyvAdapter(redisCache),
8);

Legacy caching implementation

Versions of Apollo Server prior to 3.9 use the apollo-server-caching package to implement caching. The apollo-server-caching package is no longer maintained, and we do not recommend using it. The KeyValueCache interface has been moved and is now in the @apollo/utils.keyvaluecache package.

The InMemoryLRUCache class has also moved to the @apollo/utils.keyvaluecache package. The InMemoryLRUCache class now uses version 7 of lru-cache, accepting different configuration options and no longer allowing a cache to be unbounded.

The apollo-server-cache-redis and apollo-server-cache-memcached packages are no longer receiving updates; we recommend using keyv instead, as shown above.