Skip to content

Commit

Permalink
docs-response-caching
Browse files Browse the repository at this point in the history
  • Loading branch information
n1ru4l committed Nov 8, 2022
1 parent b79a882 commit 50f7cd7
Showing 1 changed file with 43 additions and 26 deletions.
69 changes: 43 additions & 26 deletions website/src/pages/v3/features/response-caching.mdx
Original file line number Diff line number Diff line change
@@ -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.

<PackageCmd packages={['@graphql-yoga/plugin-response-cache']} />

## 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 () => {
Expand All @@ -41,42 +48,49 @@ 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%
```

## 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
Expand All @@ -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
Expand All @@ -106,7 +120,7 @@ mutation {
}
```

```json
```json filename="GraphQL operation execution result"
{
"data": {
"updateLaunch": {
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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' }
Expand All @@ -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.

<Callout>The Redis cache currently only works in Node.js environments.</Callout>

<PackageCmd packages={['@envelop/response-cache-redis']} />

```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({
Expand Down

0 comments on commit 50f7cd7

Please sign in to comment.