• v3
  • Features
  • Response Caching

Response caching is a technique for reducing server load by caching GraphQL query operation results.

Installation

yarn add @graphql-yoga/plugin-response-cache

Quick Start

import { createYoga, createSchema } from 'graphql-yoga'
import { createServer } from 'node:http'
import { useResponseCache } from '@graphql-yoga/plugin-response-cache'
 
const yoga = createYoga({
  schema: createSchema({
    typeDefs: `type Query { slow: String}`,
    resolvers: {
      Query: {
        slow: async () => {
          await new Promise((resolve) => setTimeout(resolve, 5000))
          return 'I am slow.'
        },
      },
    },
  }),
  plugins: [
    useResponseCache({
      // global cache
      session: () => null,
    }),
  ],
})
 
const server = createServer(yoga)
server.listen(4000, () => {
  console.info('Server is running on http://localhost:4000/graphql')
})
curl -X POST -H 'Content-Type: application/json' http://localhost:4000/graphql \
-d '{"query":"{__typename}"}' -w '\nTotal time : %{time_total}'

This will output something like the following:

{"data":{"slow":"I am slow."}}
Total time:5.026632

After executing the same curl statement a second time, the duration is significantly lower.

{"data":{"slow":"I am slow."}}
Total time:0.007571%

Session based caching

If your GraphQL API returns specific data depending on the viewer's session, you can use the session option to cache the response per session.

useResponseCache({
  // cache based on the authentication header
  session: (request) => request.headers.get('authentication'),
})

TTL

It is possible to give cached operations a time to live. Either globally, based on schema coordinates or object types. If a query operation result contains multiple objects of the same type, the lowest TTL is picked.

useResponseCache({
  session: () => null,
  // by default cache all operations for 2 seconds
  ttl: 2_000,
  ttlPerType: {
    // only cache query operations containing User for 500ms
    User: 500,
  },
  ttlPerSchemaCoordinate: {
    // cache operations selecting Query.lazy for 10 seconds
    'Query.lazy': 10_000,
  },
})

Invalidatios via Mutation

When executing a mutation operation the cached query results that contain type entities within the Mutation result will be automatically be invalidated.

mutation {
  updateUser(id: 1, newName: "John") {
    __typename
    id
    name
  }
}
{
  "data": {
    "updateLaunch": {
      "__typename": "User",
      "id": "1",
      "name": "John"
    }
  }
}

All cached query results that contain the type User with the id 1 will be invalidated.

This behavior can be disabled by setting the invalidateViaMutation option to false.

useResponseCache({
  session: (request) => null,
  invalidateViaMutation: false,
})

Manual Invalidation

You can invalidate a type or specific instances of a type using the cache invalidation API.

In order to use the API, you need to manually instantiate the cache an pass it to the useResponseCache plugin.

import {
  useResponseCache,
  createInMemoryCache,
} from '@graphql-yoga/plugin-response-cache'
 
const cache = createInMemoryCache()
 
useResponseCache({
  session: () => null,
  cache,
})

Then in your business logic you can call the invalidate method on the cache instance.

Invalidate all GraphQL query results that referance a specific type:

cache.invalidate([{ type: 'User' }])

Invalidate all GraphQL query results that reference a specific entity of a type:

cache.invalidate([{ type: 'User', id: '1' }])

Invalidate all GraphQL query results multiple entities in a single call.

cache.invalidate([
  { type: 'Post', id: '1' },
  { type: 'User', id: '2' },
])

External Cache

By default the response cache stores all the cached query results in memory.

If you want a cache that is shared between multiple server instances you can use the Redis cache implementation.import from '@theguild/components';

yarn add @envelop/response-cache-redis
import { useResponseCache } from '@graphql-yoga/plugin-response-cache'
import Redis from 'ioredis'
 
const redis = new Redis({
  host: 'my-redis-db.example.com',
  port: '30652',
  password: '1234567890',
})
 
const redis = new Redis('rediss://:1234567890@my-redis-db.example.com:30652')
 
const cache = createRedisCache({ redis })
 
useResponseCache({
  session: () => null,
  cache,
})
Last updated on September 27, 2022