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-graphql
directly with--experimental-modules
. It will not fix the issue when other packages such askoa-graphql
that do not yet support native ESM useexpress-graphql
internally via CJS. - Somehow fix the
graphql
assertion 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!