From 50f7cd7943fe92fdbe7020927e6c8d7c1a070565 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Tue, 8 Nov 2022 10:22:33 +0100 Subject: [PATCH] docs-response-caching --- .../pages/v3/features/response-caching.mdx | 69 ++++++++++++------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/website/src/pages/v3/features/response-caching.mdx b/website/src/pages/v3/features/response-caching.mdx index bde37b6c52..252c31d77c 100644 --- a/website/src/pages/v3/features/response-caching.mdx +++ b/website/src/pages/v3/features/response-caching.mdx @@ -1,23 +1,30 @@ -import { PackageCmd } from '@theguild/components' +import { PackageCmd, Callout } from '@theguild/components' # Response Caching Response caching is a technique for reducing server load by caching GraphQL query operation results. +For incoming GraphQL Query operations with the same variable values, the same response is returned from a cache instead of executed again. -## Installation +## Quick Start + +The response cache is a separate package that needs to be installed. -## Quick Start +The following sample setup show as slow field resolver (`Query.slow`). -```ts +```ts filename="Response cache example" {14-17,22-25} 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}`, + typeDefs: /* GraphQL */ ` + type Query { + slow: String + } + `, resolvers: { Query: { slow: async () => { @@ -41,21 +48,23 @@ server.listen(4000, () => { }) ``` -``` +After starting the server we can execute a GraphQL Query operation, that selects the `Query.slow` field. + +```sh filename="Execute slow GraphQL Query Operation with cUrl" curl -X POST -H 'Content-Type: application/json' http://localhost:4000/graphql \ --d '{"query":"{__typename}"}' -w '\nTotal time : %{time_total}' +-d '{"query":"{slow}"}' -w '\nTotal time : %{time_total}' ``` -This will output something like the following: +The output will look similar to the following: -``` +```sh filename="Initial Request time" {"data":{"slow":"I am slow."}} Total time:5.026632 ``` After executing the same curl statement a second time, the duration is significantly lower. -``` +```sh filename="Cached Request time" {"data":{"slow":"I am slow."}} Total time:0.007571% ``` @@ -63,20 +72,25 @@ 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. +Usually, the session is determined by an HTTP header, e.g. an user id within the encoded access token. + +The `session` function receives a `request` parameter that is a [`Request` object](https://developer.mozilla.org/en-US/docs/Web/API/Request). -```ts +```ts filename="Response Cache configuration based on header" useResponseCache({ // cache based on the authentication header session: (request) => request.headers.get('authentication') }) ``` -## TTL +## Time to Live (TTL) -It is possible to give cached operations a time to live. Either globally, based on [schema coordinates](https://github.com/graphql/graphql-wg/blob/main/rfcs/SchemaCoordinates.md) or object types. -If a query operation result contains multiple objects of the same type, the lowest TTL is picked. +It is possible to give cached operations a time to live. +Either globally, based on [schema coordinates](https://github.com/graphql/graphql-wg/blob/main/rfcs/SchemaCoordinates.md) or object types. -```ts +If a query operation result contains multiple objects of the same or different types, the lowest TTL is picked. + +```ts filename="Response Cache configuration with TTL" useResponseCache({ session: () => null, // by default cache all operations for 2 seconds @@ -96,8 +110,8 @@ useResponseCache({ When executing a mutation operation the cached query results that contain type entities within the Mutation result will be automatically be invalidated. -```graphql -mutation { +```graphql filename="GraphQL mutation operation" +mutation UpdateUser { updateUser(id: 1, newName: "John") { __typename id @@ -106,7 +120,7 @@ mutation { } ``` -```json +```json filename="GraphQL operation execution result" { "data": { "updateLaunch": { @@ -118,11 +132,11 @@ mutation { } ``` -All cached query results that contain the type `User` with the id `1` will be invalidated. +For the given GraphQL operation and execution result 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`. -```ts +```ts filename="Disabling mutation invalidation" useResponseCache({ session: (request) => null, invalidateViaMutation: false @@ -135,7 +149,7 @@ You can invalidate a type or specific instances of a type using the cache invali In order to use the API, you need to manually instantiate the cache an pass it to the `useResponseCache` plugin. -```ts +```ts filename="Manual cache construction" {3,6} import { useResponseCache, createInMemoryCache @@ -153,19 +167,19 @@ Then in your business logic you can call the `invalidate` method on the cache in Invalidate all GraphQL query results that reference a specific type: -```ts +```ts filename="Invalidating a type" cache.invalidate([{ type: 'User' }]) ``` Invalidate all GraphQL query results that reference a specific entity of a type: -```ts +```ts filename="Invalidate a specific entity of a type" cache.invalidate([{ type: 'User', id: '1' }]) ``` Invalidate all GraphQL query results multiple entities in a single call. -```ts +```ts filename="Invalidate multiple entities" cache.invalidate([ { type: 'Post', id: '1' }, { type: 'User', id: '2' } @@ -176,12 +190,15 @@ cache.invalidate([ 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. +If you want a cache that is shared between multiple server instances you can use the Redis cache implementation, which is available as a separate package. + +The Redis cache currently only works in Node.js environments. -```ts +```ts filename="Create a custom Redis Cache" import { useResponseCache } from '@graphql-yoga/plugin-response-cache' +import { createRedisCache } from '@envelop/response-cache-redis' import Redis from 'ioredis' const redis = new Redis({