I am getting a required error using graphql tools

I am getting a required error using graphql tools


3

I am building a gql server application using apollo server.

When I try to load my .graphql files using import { loadFilesSync } from '@graphql-tools/load-files', this works very well, but when I load my resolver files, i get an error

node:internal/errors:464
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_REQUIRE_ESM]: require() of ES Module /userpath/index.js from /userpath/server-gql/noop.js not supported.
Instead change the require of index.js in /userpath/server-gql/noop.js to a dynamic import() which is available in all CommonJS modules.
    at Object.newLoader [as .js] (/userpath/server-gql/node_modules/pirates/lib/index.js:141:7)
    at file:///userpath/server-gql/node_modules/@graphql-tools/load-files/esm/index.js:104:33
    at Array.map (<anonymous>)
    at loadFilesSync (file:///userpath/server-gql/node_modules/@graphql-tools/load-files/esm/index.js:95:10)
    at file:///userpath/server-gql/schema.js:20:24
    at async Promise.all (index 0) {
  code: 'ERR_REQUIRE_ESM'
}

I am using "type": "module" in my package.json.

Here’s my code snippet for where i get the error

import path from 'path'
import { fileURLToPath } from 'url'

import { loadFilesSync } from '@graphql-tools/load-files'
import { mergeTypeDefs, mergeResolvers } from '@graphql-tools/merge'


const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const typesArray = loadFilesSync(path.join(__dirname, '.'), {
  recursive: true,
  extensions: ['graphql'],
})



const resolversArray = loadFilesSync(path.join(__dirname, './graphql/**/*.resolvers.js'), {
  recursive: true,
  extensions: ['js'],
})



const newResolversArray = resolversArray.slice(1)



export const typeDefs = mergeTypeDefs(typesArray)
export const resolvers = mergeResolvers(newResolversArray)

I think the error occurs in the resolvers array.

2 Answers
2


0

I was running into a very similar issue and found this workaround/solution worked for me: https://github.com/ardatan/graphql-tools/issues/1750#issuecomment-655828240

I had to modify that answer a bit to get it working in my codebase…below is what my own resolverFiles snippet now looks like (I’m not 100% sure what your import/naming conventions look like, or I’d try to apply this to your code snippet).

I have a mixed, nested directory schema/ that contains resolver files of the format SomethingResolvers.ts. Each of these look like the following (I’m using codegen):

import { Resolvers } from "../../generated/graphql";

export const resolvers: Resolvers = { /* resolver implementations */ };
const resolverFiles = await loadFiles(
  path.join(__dirname, "**/*Resolvers.js"),
  {
    requireMethod: async (path) => {
      const module = await import(pathToFileURL(path).toString());

      return module["resolvers"];
    },
    recursive: true,
  }
);


0

ESM(ECMAScript modules – import/export) loading logic is asynchronous, so it seems like you can’t use loadFilesSync when using ESM.

The following code will technically work but won’t be right because it will use require to load the modules.

const resolversArray = await loadFiles(resolversPath);

So I think a more correct version will be:

import { pathToFileURL } from 'url';

const resolversArray = await loadFiles(resolversPath, {
        requireMethod: async (path) => {
            console.debug('Using resolver at:', path);
            return await import(pathToFileURL(path));
        }
});

Now, apparently if you’re using the mjs file extension for your modules, you will need to also specify a useRequire option to load the file using the requireMethod no matter the file’s extension:

import { pathToFileURL } from 'url';

const resolversArray = await loadFiles(resolversPath, {
        useRequire: true,
        requireMethod: async (path) => {
            console.debug('Using resolver at:', path);
            return await import(pathToFileURL(path));
        }
});

+You’ll need to specify the extension in either the path or the extensions option

You can see the default extensions here

I just used the extension in the path, like so:

import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const resolversPath = path.join(
        __dirname, 'resolvers', '**', '*.resolvers.mjs'
);

So now every file(recursively) inside the resolvers folder that’s inside the folder of the current file with the file extension of resolvers.mjs will be loaded.

Oh, on an unrelated note, you can use mergeResolvers from the @graphql-tools/merge package to merge needed resolvers gracefully(resolvers under Query for example).

Full example:

import { loadFiles } from '@graphql-tools/load-files';

import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

async function getResolvers() {
    const resolversPath = path.join(
        __dirname, 'resolvers', '**', '*.resolvers.mjs'
    );
    const resolversArray = await loadFiles(resolversPath, {
        useRequire: true,
        requireMethod: async (path) => {
            console.debug('Using resolver at:', path);
            return await import(pathToFileURL(path));
        }
    });
    return mergeResolvers(resolversArray);
}

const resolvers = await getResolvers();
console.debug('GraphQL used resolvers:');
console.debug(resolvers);

New contributor

Barni is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



Leave a Reply

Your email address will not be published. Required fields are marked *