Search code examples
node.jsmongodbgraphqlapolloapollo-server

How to separate GraphQL schema and resolver files using Apollo?


I have this simple GraphQL Apollo server working when it's all within a single file, but I am having trouble getting it to work when "modularizing" the schema and resolvers to their own files.

/index.js

require('dotenv').config()
const { MongoClient } = require('mongodb')
const { ApolloServer, makeExecutableSchema } = require('apollo-server')

const { Query } = require('./resolvers/Query')

// database connection
MongoClient.connect(process.env.DB_URL, {
  poolSize: 25,
  useUnifiedTopology: true,
  wtimeout: 2500
}).catch(error => {
  console.log('Error connecting to MongoDB.', error.stack)
  process.exit(1)
}).then(async client => {
  console.log(`Connected to database: ${process.env.DB_NAME}`)

  const dbConnection = client.db(process.env.DB_NAME)

  const resolvers = {
    Query
  }

  const schema = makeExecutableSchema({
    typeDefs: './schema.graphql',
    resolvers
  })

  const server = new ApolloServer({
    schema: schema
  })

  server.listen().then(({ url }) => {
    console.log(`Server ready at ${url}.`)
  })
})

/schema.graphql

type CourseType {
  _id: String!
  name: String!
}

type Query {
  courseType(_id: String): CourseType
  courseTypes: [CourseType]!
}

/resolvers/Query.js

const { ObjectId } = require('mongodb')

const courseTypesCollection = dbConnection.collection(process.env.DB_COLLECTION_COURSE_TYPES)

const Query = {
  courseType: async (root, { _id }) => {
    return courseTypesCollection.findOne(ObjectId(_id))
  },
  courseTypes: async () => {
    return courseTypesCollection.find({}).toArray()
  }
}

module.exports = {
  Query
}

When I try to run the index.js file, I get the error: UnhandledPromiseRejectionWarning: GraphQLError: Syntax Error: Cannot parse the unexpected character ".". This seems like I'm not properly loading/using the schema file. I would like to have this working without adding more libraries, if apollo-server can handle this on it's own.


Solution

  • makeExecutableSchema expects typeDefs to be either a string of the type definitions, a DocumentNode object representing the type definitions parsed into AST, an array of either of the former will be merged together. Here it seems you are passing a file path to typeDefs instead of the content of the file.

    You could change the /schema.graphql file to a simple code file like this for example:

    const schema = `
      type CourseType {
        _id: String!
        name: String!
      }
    
      type Query {
        courseType(_id: String): CourseType
        courseTypes: [CourseType]!
      }
    `
    module.exports = schema
    

    then load it to typedef

      const graphQLSchema = require('./schema.graphql')
      //...
    
      const schema = makeExecutableSchema({
        typeDefs: graphQLSchema,
        resolvers
      })
    

    You could load multiple schemas this way if you pass an array to typeDefs