Seperate Typedefs and Resolvers in to Files Graphql – Apollo Server

Modularizing Graphql Schema is a must if you plan to go big. Modularizing mean separating all TypeDefs and Resolvers of Graphql in to separate files, so that we can easily add more and more schema without jamming. Here we are using Mongoose as the database. Feel free to use your own database preference like MySql.

The method I used was based on Graphql Apollo Server. You can still used that with Grapqhl Yoga too. But I suggest you to go with Apollo because Graphql Yoga too based on Apollo and now Apollo is far more better that Graphql-yoga.

Step 1: Enable ES6 support for Graphql Apollo Server

We assume that you already have setup your Apollo Graphql Server. Here we just adding ES6 support to Apollo Graphql Server to make modularizing easier and understandable. 

First install ESM package

 npm i --save esm

Now run Grapqhl Server with ES6 support. Here I am using Nodemon to auto reload Graphql server every time I made a change.


    npm -r esm index.js
    # if using nodemon
    nodemon -r esm index.js
     

Non Seperated Schema - Example used to modularize

Below shows the index.js file used to modularize. Below shows simple todo application.  Image that Todo is part of a huge app and we want to seperate Todo app code from rest of other codes. So we are separating Todo relatated Schema in to separate files.

const { ApolloServer } = require('apollo-server');
import mongoose from 'mongoose';

mongoose.connect('mongodb://localhost/ToDos',{ useNewUrlParser: true });
var db = mongoose.connection;
//Creating Model
var Todo = mongoose.model('Todo', {
    text:String,
    complete:Boolean
});


//End of DB Model

const typeDefs = `
  type Query {
    todos:[Todo]
  }
  type Todo{
    id:ID!  
    text: String!
    complete:Boolean!
  }
  type Mutation {
      createTodo(text:String!):Todo
      updateTodo(id:ID!, complete:Boolean!) : Boolean
      removeTodo(id:ID!): Boolean
  }
`;

const resolvers = {
  Query: {
    todos: () => Todo.find()
  },
  Mutation:{
      createTodo: async (_, { text } ) => {
        const todo = new Todo({ text, complete: false });
        await todo.save();
        return todo;
      },
      updateTodo: async (_, { id, complete }) => {
        await Todo.findByIdAndUpdate(id,{complete});
        return true;
      },
      removeTodo: async(_, { id}) => {
        var res = await Todo.findByIdAndDelete(id,function(err){
          if(err) return false;
          return true;
        }).then(result => {
          return true;
        })
        .catch( err => {
          return false;
        });
        if(res) return true; else return false;
      }
  }
};

const server = new ApolloServer({ schema });

db.once('open', function() {
    //Starting QL server once connected to Mongo DB
    server.listen().then(({ url }) => {
      console.log(`🚀  Server ready at ${url}`);
    });    
});

Step 2: Seperating TypeDefs

In Graphql we use Type Query to write our queries like mutations. All Queries goes inside the Type Query. Since we are seperating Queries into separate files 

  1. First we need to define empty type Query in index.js
  2. And then extend Query using extend type Query
  3. Then create empty type Mutation in index.js
  4. And used extend mutation using extend type Mutation

Note: Mongoose was import to used on resolvers later.

###File - schema/schema_todo.js
import mongoose from 'mongoose';

export const typeDefs = `
  extend type Query{
    todos:[Todo]
  } 
  type Todo{
    id:ID!  
    text: String!
    complete:Boolean!
  }
  extend type Mutation {
      createTodo(text:String!):Todo
      updateTodo(id:ID!, complete:Boolean!) : Boolean
      removeTodo(id:ID!): Boolean
  }`;
###File - index.js
 
......
type Query {
    _empty: String
  }
  type Mutation {
    _empty: String
  }
};
.....
  

Step 3: Sperating Resolvers

Below show complete schema_todo.js file with seperated typeDefs and Resolvers. There is nothing fancy about resolvers. Just adding export to beginning. That’s all. Here with resolvers i have cut and paste the codes related to mongoose too.

###File - schema/schema_todo.js
 
import mongoose from 'mongoose';
mongoose.connect('mongodb://localhost/ToDos',{ useNewUrlParser: true });

var db = mongoose.connection;
//Creating Model
var Todo = mongoose.model('Todo', {
    text:String,
    complete:Boolean
});

export const typeDefs = `
  extend type Query{
    todos:[Todo]
  } 
  type Todo{
    id:ID!  
    text: String!
    complete:Boolean!
  }
  extend type Mutation {
      createTodo(text:String!):Todo
      updateTodo(id:ID!, complete:Boolean!) : Boolean
      removeTodo(id:ID!): Boolean
  }
  `;

  export const resolvers = {
    Query: {
      todos: () => Todo.find()
    },
    Mutation:{
        createTodo: async (_, { text } ) => {
          const todo = new Todo({ text, complete: false });
          await todo.save();
          return todo;
        },
        updateTodo: async (_, { id, complete }) => {
          await Todo.findByIdAndUpdate(id,{complete});
          return true;
        },
        removeTodo: async(_, { id}) => {
          var res = await Todo.findByIdAndDelete(id,function(err){
            if(err) return false;
            return true;
          }).then(result => {
            return true;
          })
          .catch( err => {
            return false;
          });
          if(res) return true; else return false;
        }
    }
  };

In index.js you can defind common Queries and Mutations like inteface types

###File - index.js
 
......
const resolvers = {
    Query: { 
      
    },
    Mutation:{
     
    },
};
.....
  `;

Step 4: Import Seperated typeDefs and Resolvers to make final Schema

Below show the find index.js file. After here you can seperate all schema creating code to another file like schema.js. It’s up to you. 

Here I have imported the todo typeDefs and resolvers and join them. You can use same procedure to join more typedefs and resolvers.

  1. Import merge from lodash
  2. Import MakeExeccutableSchema from graphql-tools
  3. Import all TypeDefs and Resolvers
  4. Using makeExecutableSchema merge all TypeDefs and Resolvers
  5. Make sure not to change name “schema” used to add when creating ApolloServer
const { ApolloServer } = require('apollo-server');
const { merge } = require('lodash');
const { makeExecutableSchema } =  require('graphql-tools');

//Typedefs and Resolvers
import {typeDefs as TodoDefs, resolvers as TodoResolvers} from "./schema/schema_todo.js";

//End of DB Model
const typeDefs = `
  type Query {
    _empty: String
  }
  type Mutation {
    _empty: String
  }
`;

const resolvers = {
    Query: { 
      
    },
    Mutation:{
     
    },
};

const schema = makeExecutableSchema({
    typeDefs:[Query,TodoDefs],
    resolvers:merge(resolvers,TodoResolvers)
  });

  const server = new ApolloServer({ schema });

  db.once('open', function() {
      //Starting QL server once connected to Mongo DB
      // This `listen` method launches a web-server.  Existing apps
      // can utilize middleware options, which we'll discuss later.
      server.listen().then(({ url }) => {
        console.log(`🚀  Server ready at ${url}`);
      });    
  });

That’s all. feel free to leave a comment. 

Leave a Reply

avatar
  Subscribe  
Notify of