graphql-tag: Typescript: import fail: Cannot find module 'query.gql'

This is a reopen of https://github.com/apollographql/graphql-tag/issues/42 .

Nothing has changed, it’s just that the presence of a workaround is not a fix.

As described before, attempts to import a graphql module result in Cannot find module.... Using require instead of import, eg:

const query = require('gql/Query.gql');

works fine… up to a point. Somewhere, the Apollo graphql decorator performs a deep copy of the query The query DocumentNode returned above includes a default element that has itself as a property. As a result, the deep copy blows out with a soft stack overflow. So the full workaround is:

const query = require('gql/Query.gql'); delete query['default']

It’s possible that the default problem is dependent on the tsconfig.json setting:

"allowSyntheticDefaultImports": true

I need it for other reasons, and was unable to easily test with it turned off.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 28
  • Comments: 33 (2 by maintainers)

Commits related to this issue

Most upvoted comments

This solution works for me. It’s similar to what @ddetkovs posted earlier but allows you to use the import syntax and doesn’t require using /// <reference path="..." />

All of my .graphql files are in a single folder so I could add an index.d.ts in that folder with the following

declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export = value;
}

Having all of the .graphql files in a single directory and naming it index.d.ts avoids the need to add /// <reference path="./graphql.d.ts" /> in other files.

I can then import using import * as MyQuery from "../graphql/MyQuery.graphql"

Thanks to @ddetkovs, @Aides359

Changing the solution by @ddetkovs to

// graphql.d.ts file
declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export default value;
}
/// <reference path="./graphql.d.ts" />
import schema from './schema.graphql';

works for ES2015 import.

I found that this works:

// graphql.d.ts file
declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export = value;
}

then, when I need to import *.graphql

/// <reference path="./graphql.d.ts" />
import schema = require('./schema.graphql');

I am also having the same issue. I have also tried workarounds mentioned in #42 and here #59 . Also tried adding custom module declaration for gql files and playing around with alternatives.

Workarounds and outputs are like these:

  1. Naive approach: Try direct import query.gql
import query from './query.gql'

/* =>

Ts output: `error TS2307: Cannot find module './query.gql'.`
Browser console output: -

*/
  1. Add wildcard custom declaration to index.t.ds
// index.d.ts
declare module "*.gql" {
  const content: any;
  export default content;
}

// component.tsx
import query from './query.gql'

/* =>

Ts output: Fine / No error
Browser console output: browser.js:40 Uncaught Error: Argument of undefined passed to parser was not a valid GraphQL DocumentNode. You may need to use 'graphql-tag' or another method to convert your operation into a document

*/
  1. Try with import = require syntax.
// index.d.ts
// // ...same as 2

// component.tsx
import query = require('./query.gql')

/* =>

Ts output: `error TS2345: Argument of type 'typeof "*.gql"' is not assignable to parameter of type 'DocumentNode'.
  Property 'kind' is missing in type 'typeof "*.gql"'.`
Browser console output: Fine / No error (actually works)

*/
  1. Try adding type declaration DocumentNode from graphql typings.
// index.d.ts
import {
  DocumentNode
} from 'graphql'

declare module "*.gql" {
  const content: DocumentNode;
  export default content;
}

// component.tsx
import query = require('./query.gql')

/* =>

Ts output: `error TS2307: Cannot find module './query.gql'.`
Browser output: -

*/

I can live with typescript error Argument of type 'typeof "*.gql"' is not assignable since it does not break actual usage, but it would be great to fix that in some way.

I have tried both "allowSyntheticDefaultImports": true and delete query['default'] workarounds, could not get an errorless flow for both typescript and browser usage.

@buggy thanks. After add d.ts,tslint still throw some error:

My case is:


import * as Q from 'gqlMod/queries/library.gql';
import * as M from 'gqlMod/mutations/library.gql';

//...
//...

export default compose(
  graphql(Q.CART, {
    props: ({ data: { cart } }) => {
      return {
        cart
      };
    }
  }),
  graphql(M.REMOVE_ALL_FROM_COUNT, { name: 'removeAllFromCart' }),
  graphql(M.ADD_TO_CART, { name: 'addToCart' }),
  graphql(M.REMOVE_FROM_CART, { name: 'removeFromCart' }),
  graphql(M.REMOVE_COUNT_FROM_CART, { name: 'removeCountFromCart' })
)(Cart);

tslint give me some errors:

[ts] Property 'CART' does not exist on type 'DocumentNode'. [ts] Property 'REMOVE_ALL_FROM_COUNT' does not exist on type 'DocumentNode'.

So I change the d.ts like this:

declare module '*.gql' {
  import { DocumentNode } from 'graphql';

  const value: {
    CART: DocumentNode;
    REMOVE_ALL_FROM_COUNT: DocumentNode;
    ADD_TO_CART: DocumentNode;
    REMOVE_FROM_CART: DocumentNode;
    REMOVE_COUNT_FROM_CART: DocumentNode;
  };
  export = value;
}

It works fine. tslint error gone. It’s a little more complex for adding definition for each query.

So change d.ts again like this:

declare module '*.gql' {
  import { DocumentNode } from 'graphql';

  const value: {
    [key: string]: DocumentNode;
  };
  export = value;
}

I don’t see it mentioned here, and this is the top google result for this problem. So FYI, graphql-code-generator has a plugin to generate these module definitions. It solved the problem for me. https://graphql-code-generator.com/docs/plugins/typescript-graphql-files-modules

I used code from @buggy:

declare module '*.graphql' {
  import { DocumentNode } from 'graphql';

  const value: DocumentNode;
  export = value;
}

I put this code into index.d.ts file into my src/@types folder. Then I add this folder to my typeRoots setting in tsconfing.json file:

"typeRoots": ["./src/@types"]

This solution works for me for all .graphql files inside my src folder.

Faced the problem with importing .gql across many directories (same as @switz has mentioned).

Solved it with global.d.ts. For example, let it be placed in src folder

// src/global.d.ts
declare module '*.gql' {
    import { DocumentNode } from 'graphql';

    const value: DocumentNode;
    export = value;
}

Be sure that src folder is in include section of your tsconfig.json

// tsconfig.json
{
    // ...
    "include": ["src/**/*.ts"]
}

Thanks to @buggy and others.

@Aides359 graphql loader exports schema like this: module.exports = doc;

This module is then resolved with webpack and since you’re using es2015 import syntax it tries to access its default member, which is undefined, thus causing a runtime error.

I get the following generated code:

var graphql_tools_1 = __webpack_require__(6);
var schema_graphql_1 = __webpack_require__(4);
var resolvers_1 = __webpack_require__(1);
exports.default = graphql_tools_1.makeExecutableSchema({ typeDefs: [schema_graphql_1.default], resolvers: resolvers_1.default });

Also, typescript handbook says this:

When importing a module using export =, TypeScript-specific import module = require(“module”) must be used to import the module.

http://www.typescriptlang.org/docs/handbook/modules.html

@gustavoalfaro Did you include the declare module '*.graphql'… declaration suggested above? I figure that with 20 up-votes, it’s probably worth a shot! What error do you see?

i got the imported module is just a string? just like that “/static/media/postsByUser.79323037.graphql”

@annguyen0505 I got the same problem and you probably aren’t using graphql-tag/loader in your webpack configuration.

Check out react-app-rewired with react-app-rewire-graphql-tag so you don’t need to eject your react app (assuming you have a project created by create-react-app).