info.cacheControl is not passed to resolvers when using mergeSchemas.
https://github.com/edorivai/apollo-server-cachecontrol-bug
const schema = makeExecutableSchema({
typeDefs: `...truncated...`,
resolvers: {
Query: {
books: (_, args, __, info) => {
// Logs `undefined` when using mergeSchemas
console.log(info.cacheControl);
return books;
}
}
}
});
const merged = mergeSchemas({ schemas: [schema] });
const app = new Koa();
app.use(koaBody());
const router = new Router();
router.post('/graphql', graphqlKoa({
schema, // works; logs an object for info.cacheControl
// schema: merged, // does not work; logs undefined for info.cacheControl
context: {
foo: 'bar'
},
tracing: true,
cacheControl: true
}));
$ yarn list --pattern apollo
yarn list v1.5.1
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
$ yarn list --pattern graphql
yarn list v1.5.1
โโ [email protected]
โโ [email protected]
โโ [email protected]
We are having similar issues, but we are constructing our schema using makeExecutableSchema. Our dependencies are
$ yarn list --pattern apollo; yarn list --pattern graphql
yarn list v1.6.0
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โจ Done in 1.20s.
yarn list v1.6.0
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โโ [email protected]
โจ Done in 1.19s.
If we could get in memory caching working, we would start investigating moving to redis in future. However, the current situation does not increase my level of confidence.
We are having the same problem. Any news on this issue?
It turns out I implemented a solution on my own side.
I am basically building the local schema, then merging it with remote schema with mergeSchemas (because it's easier to get the merged schema definition) and finally enhancing the merged schema defintion with my resolvers + a resolver that is responsible for delegating queries to the remote graphql service.
This way, info.cacheControl is available in all resolvers.
// index.js
import { printSchema } from 'graphql'
import { makeExecutableSchema, mergeSchemas } from 'graphql-tools'
import typeDefs, { resolvers } from '../my-local-schema'
import getRemoteSchema, { withDelegationResolvers } from './my-remote-schema'
export default () => getRemoteSchema().then((remoteSchema) => {
const localSchema = makeExecutableSchema({ typeDefs, resolvers })
const mergedSchema = mergeSchemas({ schemas: [remoteSchema, localSchema] })
const mergedTypeDefs = printSchema(mergedSchema)
const enhancedResolvers = withDelegationResolvers(resolvers, remoteSchema)
const myAwesomeSchema = makeExecutableSchema({
typeDefs: mergedTypeDefs,
resolvers: enhancedResolvers
})
})
// my-remote-schema.js
import fetch from 'node-fetch'
import { HttpLink } from 'apollo-link-http'
import {
delegateToSchema,
FilterRootFields,
introspectSchema,
makeRemoteExecutableSchema,
transformSchema
} from 'graphql-tools'
const remoteResolver = schema => (parent, args, context, info) => delegateToSchema({
schema,
operation: info.operation.operation,
fieldName: info.fieldName,
args,
context,
info
})
export const withDelegationResolvers = (resolvers, remoteSchema) => {
const { Query = {}, ...otherResolvers } = resolvers
const remoteResolver = remoteSchema
? remoteResolver(remoteSchema)
: f => f
return {
...otherResolvers,
Query: {
...Query,
remoteSchemaRootField: remoteResolver
},
}
}
export default () => {
const link = new HttpLink({ uri: remoteGraphqlHost, fetch })
return introspectSchema(link).then((schema) => {
const remoteSchema = makeRemoteExecutableSchema({ schema, link, fetch })
return transformSchema(remoteSchema, [
new FilterRootFields((operation, rootField) => rootField === 'remoteSchemaRootField')
])
})
}
@eberhara Nice solution!
Can you provide what do you use for the remoteSchemaRootField in your typedefs?
I keep getting this error:
Query.remoteSchemaRootField defined in resolvers, but not in schema
And this makes sense, since I have not defined it anywhere in the schema.
Any advice will be welcomed :)
hey @robertistok . Glad you liked it!
This remoteSchemaRootField is an abstract name of a root field coming from the introspection of the other graphql backend.
Imagine I have 2 graphql APIs: Projects (local) and Users (remote, which I will run the introspection query against to).
Then this would be the root query of my local API:
type Query {
project(projectId: ID!): User
}
And this would be the root query of the remote API:
type Query {
user(id: ID!): User
}
In this case, the enhancedResolvers would look something like this:
const withDelegationResolvers = (resolvers, remoteSchema) => {
const { Query = {}, ...otherResolvers } = resolvers
const remoteResolver = remoteSchema
? remoteResolver(remoteSchema)
: f => f
return {
...otherResolvers,
Query: {
...Query,
user: remoteResolver
},
}
}
And the function to fetch the remote schema would filter the root query to include only the root fields that you want to expose through your API (in this case, I want to expose only user along with my Project API):
export default () => {
const link = new HttpLink({ uri: 'http://user.com/graphql', fetch })
return introspectSchema(link).then((schema) => {
const remoteSchema = makeRemoteExecutableSchema({ schema, link, fetch })
return transformSchema(remoteSchema, [
new FilterRootFields((operation, rootField) => rootField === 'user')
])
})
}
What do you think?
@eberhara
Thanks for the explanation, now I got it :) For some weird reason, I have not thought about the fact that remoteSchemaRootField has to be defined in the remote schema.
I am looking for a solution, where I can pass down the cache setting from the Apollo server to the remote schemas. This may be a good solution with some more tweaking.
Do you have any idea what would be the best way to pass down all the remote resolvers to the main schema?
This bug extends further than cacheControl -- it affects all extensions. It looks like it does call the extension eventually, but it is out of order (some time after the resolver).
This is known issue with how mergeSchemas works and is also why we have deprecated graphql-tool in favor of federation which will support cache control and tracing ๐