keystone
keystone copied to clipboard
Unable to extend `ApolloServer` context via `graphql.apolloConfig.context`
Steps to reproduce
- Use
configfrom@keystone-6/core - Initialise Keystone config
- Pass
contextfunction tographql.apolloConfig.context - Return custom
contextobject from the function - Try accessing any property from the custom
contextin any resolver - Custom
contextvalue isundefined(only default Keystonecontextvalues are present)
What should happen?
ApolloServer context should be extended with the custom context provided in graphql.apolloConfig.context as typings allow it. If provided, it should be merged with the default Keystone context, or an alternative API to extend the GQL context should be available.
What actually happens?
ApolloServer instance is passed only the default Keystone context. The value from graphql.apolloConfig.context is never used.
System Info
- macOS 13.1
- node.js 16.19.0
- @keystone-6/core 4.0.1
I had a similar issue when trying to set cacheHint (maxAge, scope):
This code won't set headers for http cache header
import { list } from "@keystone-6/core";
import { image, text } from "@keystone-6/core/fields";
import { CacheScope } from "apollo-server-types";
export default list({
fields: {
title: text({ validation: { isRequired: true } }),
image: image({
storage: "images",
graphql: { cacheHint: { maxAge: 60000, scope: CacheScope.Public } },
}),
},
});
Also can't pass scope as a string and I've imported the related enum from apollo-server-types which is weird!
There is a link on the official keystone's docs refering to this apollo document which mentions that setting cache should generate a graphql that affects that particular field.
For the example above the graphql still is:
type Image {
id: ID!
title: String
image: ImageFieldOutput
}
but It should actually be:
type Image {
id: ID!
title: String
image: ImageFieldOutput @cacheControl(maxAge: 60000, scope: PRIVATE)
}
Sorry if this was supposed to be another issue, I just thought bringing it up here is helpful.
Hey @devmor-j, your comment is not related to this issue, however, I would like to mention these points:
- I believe you have misunderstood the Apollo docs. You can set the field caching either by using GQL directives in your GQL schema (static definition) or you can specify the caching in your resolver (dynamic definition). Keystone uses the latter one, caching settings are set with the resolver and are NOT specified via directives in the GQL schema. This means your auto-generated GQL schema has no issues, directives are not needed there as the caching setup happens dynamically on field resolver.
- The
scopeproperty is of typeenum CacheScope. You cannot assign a string to an enum, TSC checks this correctly for you. It is not a bug, it's a compiler feature and it's absolutely correct it makes you import theenumfrom the package where it's originally defined. That way your code stays type safe and in sync with all the type definitions.
Thanks for your comment @rozsival
@rozsival
Have you tried graphql-middleware?
It can be used at extendGraphQLSchema
import { applyMiddleware } from 'graphql-middleware'
const useContext = async (resolve, root, args, context, info) => {
const myContext = { ...context, isFoo: true }
const result = await resolve(root, args, myContext, info)
console.log({ result })
return result
}
export const extendGraphQLSchema = (schema: GraphQLSchema) => {
return applyMiddleware(schema, useContext)
}
In my tests, it works. But the extendGraphQLSchema is not asynchronous. It causes delay and it's not scalable.
I'm trying a possible solution for the main problem of the issue.
- Example of using NexusJS: https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-schema-nexus
- Documentation of NexusJS makeSchema: https://nexusjs.org/docs/api/make-schema
- Relational StackOverflow question: https://stackoverflow.com/a/65540017