I have two node servers both serving graphql via express-graphql
.
I wish to have my first server stitch its schema with that of the second server.
I have followed these instructions at: https://www.apollographql.com/docs/graphql-tools/remote-schemas
as I am using graphql-tools.
I am able to retrieve the schema from my second node server but as soon as I try to print it (with a mere console.log
) i get this error:
Uncaught exception TypeError: schema.getDirectives is not a function
at printFilteredSchema (/Users/cchabert/git-repo/client-configuration-api/node_modules/graphql/utilities/schemaPrinter.js:61:27)
at Object.printSchema (/Users/cchabert/git-repo/client-configuration-api/node_modules/graphql/utilities/schemaPrinter.js:47:10)
at makeRemoteExecutableSchema (/Users/cchabert/git-repo/client-configuration-api/node_modules/graphql-tools/dist/stitching/makeRemoteExecutableSchema.js:60:30)
at schema (/Users/cchabert/git-repo/client-configuration-api/app/api/schema.js:68:24)
at module.exports (/Users/cchabert/git-repo/client-configuration-api/app/routes.js:102:13)
at Object.<anonymous> (/Users/cchabert/git-repo/client-configuration-api/app/index.js:15:20)
at Module._compile (internal/modules/cjs/loader.js:799:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:810:10)
at Module.load (internal/modules/cjs/loader.js:666:32)
at tryModuleLoad (internal/modules/cjs/loader.js:606:12)
at Function.Module._load (internal/modules/cjs/loader.js:598:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:862:12)
at internal/main/run_main_module.js:21:11
which causes the server to die but I am also able to see the schema somehow:
received schema: {"_queryType":"Query","_mutationType":"Mutation",
"_subscriptionType":null,"_directives":["@skip","@include","@deprecated"],
"_typeMap":{"Query":"Query","String":"String","Client":"Client",
"ID":"ID","DateTime":"DateTime",[...]}
I do see the _directives
field in the remote schema definition but the docs isn't super clear on how to deal with this.
I have looked through the github issues of the graphql-tools
repo as well but couldn't find anything.
Here is a code snippet:
const {
addMockFunctionsToSchema,
makeExecutableSchema,
makeRemoteExecutableSchema,
introspectSchema,
mergeSchemas
} = require('graphql-tools')
const _ = require('lodash)
const { createHttpLink } = require('apollo-link-http')
const fetch = require('node-fetch')
[..]
const customFetch = (uri, options = {}) => {
const httpOptions = _.merge(options, {
headers: {
'Content-type': 'application/json'
}
})
return fetch(uri, httpOptions)
}
function schema() {
const Query = `
type Query {
_empty: String
}
type Mutation {
_empty: String
}
`
const resolvers = {}
const mocks = {}
const localSchema = makeExecutableSchema({
typeDefs: [Query, [...]],
resolvers: [resolvers, [...]]
}) // by itself this schema works without any issues
const mergedMocks = _.merge(mocks, [...])
addMockFunctionsToSchema({
schema: localSchema,
mocks: mergedMocks,
preserveResolvers: true
})
const infoApiLink = createHttpLink({ uri, fetch: customFetch })
const remoteSchema = makeRemoteExecutableSchema({
schema: introspectSchema(infoApiLink).then(remoteSchema => {
console.log('received schema: ', JSON.stringify(remoteSchema))
return remoteSchema
}),
link: infoApiLink
})
return mergeSchemas({ schemas: [localSchema, remoteSchema] })
}
module.exports = {
schema
}
I would also like to make this work using only Promises (no async/await) as mentioned in https://github.com/apollographql/graphql-tools/blob/master/docs/source/remote-schemas.md#--introspectschemafetcher-context
Any suggestions welcome.
makeRemoteExecutableSchema
should be passed an instance of a GraphQLSchema
, but you're not doing that -- what you are doing is passing it a Promise that will resolve to a GraphQLSchema
, which won't work. When you call introspectSchema
, it has to make an introspection call, which is done asynchronously. It returns a Promise that resolves to the resulting GraphQLSchema
object. We need to use await
or then
in order to get that value and then we can use it as needed.
The unnecessarily messy way without async/await:
function schema () {
// ... rest of your code
return introspectSchema(infoApiLink).then(schema => {
const remoteSchema = makeRemoteExecutableSchema({ schema, link: infoApiLink })
return mergeSchemas({ schemas: [localSchema, remoteSchema] })
})
}
Or using async/await:
async function schema () {
// ... rest of your code
const schema = await introspectSchema(infoApiLink)
const remoteSchema = makeRemoteExecutableSchema({ schema, link: infoApiLink })
return mergeSchemas({ schemas: [localSchema, remoteSchema] })
}
Either way, keep in mind by calling your schema
function you will be returning a Promise that will resolve to the value returned by mergeSchemas. So where before you could have imported the function, called it and used the result directly, you will once again have to use either then
or await
to grab the value the Promise resolves to first:
import { schema } from './some-module'
schema()
.then(schema => {
const server = new ApolloServer({ schema })
server.listen()
})
.catch(error => {
// handle the Promise rejecting
})