graphql-hive / graphql-hive/envelop

[Response Cache] Better collection and empty values invalidation workflow

Open
#1,979 10 comments 0 reactions 1 assignee Claimed by @EmrysMyrddin View on GitHub
kind/enhancement
Dominant language
No language data
Stars
827
Forks
132
PR merge metrics
No merged PRs in 30d

Description

The workflow about how to handle cache invalidation for lists and empty values is not very clear.

Today, once #1961 will be merged, calling `cache.invalidate({ typename: 'Type' })` allows to invalidate response containing any entity of the given type. This is for now the most straight forward way to handle cache invalidation of responses containing list or `null` values.

But doing so also invalidate responses that are actually not affected by the fact that a new entity has been created. Like querying an existing entity.

## Example

```ts
const cache = createInMemoryCache()

const yoga = createYoga({
schema: createSchema({
typeDefs: /* GraphQL */`
type Query {
users: [User]!
user: User
}

type Mutation {
addUser(id: String!, name: String!) User
}

type User {
id: String!
name: String!
}
`,
resolvers: {
Query: {
users: () => db.listUsers(),
user: (_, { id }) => db.getUser(id),
},
Mutation: {
addUser: async (_, { id, name }) => {
const newUser = await db.createUser({ id, name })
cache.invalidate({ typeName: 'User' })
return newUser
}
}
},
}),
plugins: [
useResponseCache({
session: () => null,
cache,
})
],
})
```

This allows to correctly invalidate this kind of queries when adding a user:

### Query flow examples
#### With list
```
query {
users { id, name }
}

=> { data: { users: [{ id: '1', name: 'User 1' }] } }

mutation {
addUser(id: '2', name: 'User 2') { id }
}

=> { data: { addUser: { id: '2' } } }

query {
users { id, name }
}

with invalidation, cache miss => { data: { users: [{ id: '1', name: 'User 1' }, { id: '2', name: 'User 2' ] } }
without invalidation, cache hit => { data: { users: [{ id: '1', name: 'User 1' }] } }
```

#### With null
```
query {
user(id: 2) { id, name }
}

=> { data: { user: null } }

mutation {
addUser(id: '2', name: 'User 2') { id }
}

=> { data: { addUser: { id: '2' } } }

query {
user(id: 2) { id, name }
}

with invalidation, cache miss => { data: { user: { id: '2', name: 'User 2' } } }
without invalidation, cache hit => { data: { user: null } }
```

#### With an existing entity
```
query {
user(id: 1) { id, name }
}

=> { data: { user: null } }

mutation {
addUser(id: '2', name: 'User 2') { id }
}

=> { data: { addUser: { id: '2' } } }

query {
user(id: 1) { id, name }
}

with invalidation, cache miss => { data: { user: { id: '1', name: 'User 1' } } } <= Here we miss the cache, while the response is still valid
without invalidation, cache hit => { data: { user: { id: '1', name: 'User 1' } } }
```

## Proposal

We should find a way to improve the API to allow invalidating cache only for responses containing nulls or collections.
Something in the line of `cache.invalidateCollection('Type')`.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.