graphql-hive / graphql-hive/envelop
[Response Cache] Better collection and empty values invalidation workflow
- 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.