express-graphql
express-graphql copied to clipboard
use with ES6 modules triggers assertion in graphql
The following does not work, though it seems to me it should ([email protected]):
import graphqlHTTP from 'express-graphql'; // @0.6.12
import { buildSchema } from 'graphql'; // @1.13.2
import express from 'express';
const app = express();
app.use('/graphql', graphqlHTTP({
'schema': buildSchema('type Query { hello: String }'),
'rootValue': { 'hello': () => 'Hello, world!' }
}));
app.listen(4000);
$ curl -s -X POST -H "Content-type: application/json" http://localhost:4000/graphql --data '{ "query": "{ hello }" }'|jq
{
"errors": [
{
"message": "Cannot use GraphQLSchema \"[object Object]\" from another module or realm.\n\nEnsure that there is only one instance of \"graphql\" in the node_modules\ndirectory. If different versions of \"graphql\" are the dependencies of other\nrelied on modules, use \"resolutions\" to ensure only one version is installed.\n\nhttps://yarnpkg.com/en/docs/selective-version-resolutions\n\nDuplicate \"graphql\" modules cannot be used at the same time since different\nversions may have different capabilities and behavior. The data from one\nversion used in the function from another could produce confusing and\nspurious results."
}
]
}
Creating a wrapper to require buildSchema instead:
req.js
module.exports = require('graphql').buildSchema;
And changing the initial example:
//import { buildSchema } from 'graphql';
import buildSchema from './req';
works as expected:
$ curl -s -X POST -H "Content-type: application/json" http://localhost:4000/graphql --data '{ "query": "{ hello }" }'|jq
{
"data": {
"hello": "Hello, world!"
}
}
I've been investigating this and I don't have a solution, but I'll share what I found.
The core of the problem is that there are two different ways to get a GraphQLSchema:
import { buildSchema } from 'graphql'ends up usingnode_modules/graphql/type/schema.mjs(an ES6 module).import graphqlHTTP from 'express-graphql'doesrequire('graphql'), which ends up loadingnode_modules/graphql/type/schema.js(a CJS module).
When passing a schema created with buildSchema (so an instance of GraphQLSchema defined as a ES6 module) to express-graphql, it validates that the schema is instanceOf GraphQLSchema. But because express-graphql uses the CJS version, GraphQLSchema is a different class and therefore the passed schema is not an instance of such class.
I think that the solution would be to have a index.mjs that uses import to load everything related to Graphql, so both modules end up using the same GraphQLSchema, but I'm not 100% sure
Ideally two things should happen:
- Add native ESM support to this package via
.mjs, similar tographql: https://github.com/graphql/graphql-js/pull/1244. This would fix this bug when the consumer usesexpress-graphqldirectly with--experimental-modules. It will not fix the issue when other packages such askoa-graphqlthat do not yet support native ESM useexpress-graphqlinternally via CJS. - Somehow fix the
graphqlassertion to detect and allow simultaneous use of the CJS and ESM versions of itself if they are from the same package, or just remove it. This will be pretty desirable for true CJS/ESM cross compatibility as other packages should be able to use either or both versions simultaneously.
Until the smart people figure this out, I managed to work around this issue by using custom module loader:
export function resolve(specifier, parentModuleURL, defaultResolver) {
const resolvedModule = defaultResolver(specifier, parentModuleURL);
if (specifier === 'graphql') {
resolvedModule.url = resolvedModule.url.replace('index.mjs', 'index.js');
resolvedModule.format = 'cjs';
}
return resolvedModule;
}
This will force the cjs version of grahql. (see https://nodejs.org/api/esm.html#esm_loader_hooks on how to use it).
You can actually extend the module loader further to force esm in your project. This will allow you to use js extension instead of mjs until the ESM api is less experimental and the mjs extension is hopefully no longer needed.
This library has been deprecated and this repo will be archived soon. It has been superseded by graphql-http.
Furthermore, if you seek a fully-featured, well-maintained and performant server - I heavily recommend GraphQL Yoga!