13

I have a node, express server using expressGraphql. I am trying to declare a type definition for graphql in a .graphql or .gql file, because as the type gets larger, it becomes difficult to read the string.

Here is what I have:

import testQuery from './test.graphql';

import routes from "./routes";

import { buildSchema } from "graphql";

const schema = buildSchema(testQuery);

// Root resolver
const root = {
    message: () => "Hello World!",
};

app.use(
    "/api/graphql",
    expressGraphQL({
        schema,
        graphiql: true,
    })
);

My graphql file. //test.graphql

type Book {
    message: String
}

I get an error because Typescript

Cannot find module './test.graphql'.

I have seen people doing this:

const { makeExecutableSchema } = require('graphql-tools');

const schemaFile = path.join(__dirname, 'schema.graphql');
const typeDefs = fs.readFileSync(schemaFile, 'utf8');

const schema = makeExecutableSchema({ typeDefs });

Is this the way of doing it?

So what do I need to to config typescript to be able to import, and build the schema

leogoesger
  • 3,476
  • 5
  • 33
  • 71
  • Reading the file as pure text, as you mentioned, is the best way to do it if you are not using any bundler (like webpack). This way you also have the benefit to lint and auto-format gql files easier – femanzo Sep 26 '18 at 06:42
  • Possible duplicate of [Error compiling Typescript with graphql files](https://stackoverflow.com/questions/43818401/error-compiling-typescript-with-graphql-files) – Matias Olivera Dec 11 '18 at 23:27

3 Answers3

6

You can use https://github.com/ardatan/graphql-import-node to solve this without webpack.

Install with yarn add graphql-import-node or npm install --save graphql-import-node and then either use the graphql-import-node/register hook (if you're using ts-node):

ts-node -r graphql-import-node/register index.ts

Or import it in your file right at the top like this:

import "graphql-import-node";

I chose the later in my case because I already used ts-node/register with mocha -r for my tests.

You also may need to add "esModuleInterop": true to your compilerOptions in tsconfig.json.

allesklarbeidir
  • 312
  • 3
  • 10
  • It was so hard to find this answer, somehow for me `import "graphql-import-node";"`in the index file does not work. But `ts-node -r graphql-import-node/register index.ts` does the trick – Tonio Oct 25 '22 at 13:43
2

AFAIK, there are two ways to import schema files, either 1) by reading the file directly as you describe above, or 2) by wrapping the queries in exported variables.

// bookSchema.ts <- note the file extension is .ts instead of .graphql
export default `
  type Book {
    message: String
  }
`

// anotherSchema.ts <- note the file extension is .ts instead of .graphql
export default `
  type User {
    name: String
  }
`

// main.ts
import bookSchema from 'bookSchema';
import anotherSchema from 'anotherSchema';

const schema = makeExecutableSchema({ typeDefs: [
  bookSchema,
  anotherSchema,
] });
Mikael Lirbank
  • 4,355
  • 2
  • 28
  • 25
  • 1
    and is that really the best of doing it? That means i will have to use that methods for every type file I define – leogoesger Jul 25 '18 at 22:39
  • I did not see the error message you posted. Did you remove it again? – Mikael Lirbank Jul 25 '18 at 23:31
  • 1
    Not sure this is the best way of doing it. I've been going between these two methods over a few apps I've been working on. In my current project I use .graphql files and read them with fs.readFile. It's a matter of preference I'd say. Some devtools are bettter at dealing with plain .graphql text files so that might weight in that direction. – Mikael Lirbank Jul 25 '18 at 23:34
  • This is not the answer on how to use .graphql files in typescript without webpack as it suggest to use .ts instead. – CampSafari Nov 17 '22 at 15:57
0

This answer addresses the concerns brought up by @leogoesger. Its a modular approach of creating schemas using .graphql files without needing to define multiple makeExecutableSchema calls.

The folder structure should look something like this for it to work:


    src
    - graphql
     - schema.ts
     - bar
      - barResolver.ts
      - schema.graphql
     - foo
      - fooResolver.ts
      - schema.graphql

schema.graphql contains all your type definitions. The 'feature' Resolver files contain your resolvers which is an object containing your queries and mutations.

Inside your schema.ts file you would create your merged schema like so:


    import { mergeSchemas, makeExecutableSchema } from "graphql-tools";
    import { readdirSync, lstatSync, existsSync } from "fs";
    import * as path from "path";
    import { importSchema } from 'graphql-import'
    import { GraphQLSchema } from 'graphql';

    const schemas: GraphQLSchema[] = [];

    const isDirectory = dirPath =>  existsSync(dirPath) && lstatSync(dirPath).isDirectory();
    const getDirectories = source =>
      readdirSync(source).map( name => path.join(source, name) ).filter(isDirectory)

    const folders = getDirectories( path.resolve(__dirname, './') )

    folders.forEach(folder => {
        folder = folder.substr( folder.lastIndexOf("\\")+1 )
        const {resolvers} = require(`./${folder}/${folder}Resolver`);
        const typeDefs = importSchema( path.join(__dirname, `./${folder}/schema.graphql`) );
        schemas.push(makeExecutableSchema({resolvers, typeDefs}))
    });

    const mergedSchemas = mergeSchemas({ schemas })

    export default mergedSchemas;

The idea is to get all the relative directories that exist on the same level as schema.ts then to loop through each feature name and import the respective resolver and type definition. Next we make the schema executable and add it to our schema array. Lastly we stitch the schemas together using mergeSchemas to create a single GraphQL schema from multiple API's. (See https://www.apollographql.com/docs/graphql-tools/schema-stitching for more details.)

Then you can create your server as normal

import schema from './graphql/schema';
const server = new GraphQLServer({schema: schema})
Josh Dando
  • 1,647
  • 14
  • 9