How to Combine GraphQL Type Definitions Quickly and Easily with Apollo-Server

If you’re adding more types, queries, and mutations to a GraphQL project, you may quickly find your initial type definition file growing in line length and complexity. Instead of housing all of your type definitions in one file, I have found success in breaking these out into different files based on application domain. Once all of your GraphQL components are located in different, smaller files, it becomes much easier for you (and members of your team!) to easily locate, modify, and evolve your schema. In this quick tutorial, I’ll show you how to easily split up your type definitions using base functionality of apollo-server and without the added weight and complexity of packages like graphql-import. At the end of the article, we will take a quick dive into the Apollo Server source code to investigate some of nuances of how the ApolloServer constructor translates different typeDef input types into the GraphQL schema! These may seem like easy concepts to grasp but I found limited documentation about these features and had a blast diving into the source code so wanted to share what I discovered. Let’s do it!

You can escape a giant file like this with Queries + Mutations of different resource types lobbed together!

In the beginning stages of a Apollo-Server project, you’ll probably either have your type definitions specified in the file you construct your ApolloServer in or in a single separate file. In order to start breaking those type definitions out, I went ahead and started creating files based on the general types of resources that I was going to be querying and manipulating data for. Here’s what my typeDefs folder (which holds all of my type definitions looked like) in the beginning stages of my application).

|-- createServer.js - resolvers and typeDefs construct ApolloServer
|-- resolvers - folder where all resolver definitions are held
|-- typeDefs- folder with all type definitions
    |-- index.js
    |-- organization.js
    |-- user.js
    |-- root.js - hold top level mutation and query we will extend
    |-- marketingCampaign.js
    |-- shared.js - holds common types, enums, etc. used across app

Organizations, Users, and Marketing Campaigns are the first important resources that I needed to define the types of so it made since to split them off into their own files. Shared will hold common types to be used across domains (such as date-times and currency) and I will use the Root file to start a base Mutation and Query to extend in all of the other files will all be accessible on the top level Mutation and Query key.

Here is what one of my smaller file for a user resource actually looks like.

I use graphql-tag’s (an Apollo project) gql method to translate a template literal string into a GraphQL AST (this package also allows for some nice syntax highlighting in VSCode) and then simply exported that object from the module. This way, I have all of the resource or domain specific types, queries, and mutations in one place.

It’s important to note that I am extending the Mutation and Query type. I actually have those types defined in my root file which looks like this:

This root Mutation and Query only serve on purpose — to be extended on by the Queries and Mutations in my other files. It’s a current limitation of Apollo-Server that these “base” types require having a Query or Mutation on them. If I remove line 5 and try to create an empty Query type to extend later on, Apollo will throw a syntax error. I’ve read that they will be fixing or improving this functionality in the future so stay tuned for that!

The final step that I have in order to get these combined typeDefs into my GraphQL application is to just pop them into a plain JavaScript array. I take care of this in my index file of the typeDefs folder. This is what the simple file where I combine these typeDefs look like.

the index file in my typeDefs directory