keystonejs / keystone

The superpowered headless CMS for Node.js — built with GraphQL and React
https://keystonejs.com
MIT License
9.3k stars 1.16k forks source link

Unable to extend `ApolloServer` context via `graphql.apolloConfig.context` #8303

Open rozsival opened 1 year ago

rozsival commented 1 year ago

Steps to reproduce

  1. Use config from @keystone-6/core
  2. Initialise Keystone config
  3. Pass context function to graphql.apolloConfig.context
  4. Return custom context object from the function
  5. Try accessing any property from the custom context in any resolver
  6. Custom context value is undefined (only default Keystone context values 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

devmor-j commented 1 year ago

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.

rozsival commented 1 year ago

Hey @devmor-j, your comment is not related to this issue, however, I would like to mention these points:

  1. 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.
  2. The scope property is of type enum 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 the enum from the package where it's originally defined. That way your code stays type safe and in sync with all the type definitions.
devmor-j commented 1 year ago

Thanks for your comment @rozsival

ganobrega commented 1 year ago

@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.

ganobrega commented 1 year ago

I'm trying a possible solution for the main problem of the issue.

  1. Example of using NexusJS: https://github.com/keystonejs/keystone/tree/main/examples/extend-graphql-schema-nexus
  2. Documentation of NexusJS makeSchema: https://nexusjs.org/docs/api/make-schema
  3. Relational StackOverflow question: https://stackoverflow.com/a/65540017
image