Apollo is a beautifully cohesive set of tools for quickly and efficiently building out a GraphQL-powered project. Unfortunately, that cohesiveness can cause problems when you try to do something a little out of the norm.

Recently, I ran into a problem with trying to use traditional GraphQL schema types (like GraphQLObjectType) together with an Apollo-generated schema.

Straight out of the box, these two approaches don’t play nicely together. However, I managed to put together a working, but less than ideal, solution.

Read on!

The Problem

Recently, I’ve been working on an Apollo-powered client project. Apollo schema strings are used to define types and schemas throughout the project.

While working on a new feature for the project, I began using Mongoose to model data living in a MongoDB database. Using Mongoose and GraphQL together meant I had to define two nearly identical schemas.

One schema for Mongoose:


export default new Schema({
    _id: Number,
    name: String,
    roomId: Number
});

And another for GraphQL:


export default `
    type Bed {
        _id: Int
        name: String
        roomId: Int
        room: Room
    }
`;

From past experience, I knew that having to maintain both schemas would be a nightmare. They would inevitably diverge, either through laziness, forgetfulness, or ignorance, and that divergence would lead to problems down the road.

The solution to this problem, in my mind, was to generate the GraphQL schema from the Mongoose model.

Mongoose Schema to GraphQL

Thankfully, after a quick search I found a Node.js package to do exactly that: Sarkis Arutiunian’s mongoose-schema-to-graphql.

As you would expect, the createType function in the mongoose-schema-to-graphql package accepts a Mongoose schema as an argument and returns a corresponding GraphQLObjectType object as a result.

Armed with this tool, I could replace the Bed GraphQL type definition with a call to createType:


export default () => [
    createType(Bed.schema),
    `
        extend type Bed {
            room: Room
        }
    `
];

At least that was the dream…

Unfortunately, trying to pass the generated GraphQLObjectType into Apollo’s makeExecutableSchema fails. The typeDefs option in makeExecutableSchema only accepts an array of GraphQL schema strings, or a function that returns an array of GraphQL schema strings.

Trying to pass in a GraphQLObjectType causes makeExecutableSchema to blow up.

A (Less Than Ideal) Solution

Our mongoose-schema-to-graphql package only returns raw GraphQL types, and makeExecutableSchema only accepts schema strings.

Short of forking and extending Apollo’s graphql-tools, it seems like our only option is to convert the GraphQLObjectType returned by createType into a string before handing it off to makeExecutableSchema.

But how do we convert a GraphQLObjectType into a corresponding GraphQL schema string?


export default () => [
    printType(createType(Bed.schema)),
    `
        extend type Bed {
            room: Room
        }
    `
];

The printType utility function in Facebook’s graphql package saves the day!

We can use the printType function to convert the GraphQLObjectType generated from our Mongoose model into a GraphQL schema string which we can then drop into makeExecutableSchema.

Unfortunately, makeExecutableSchema immediately undoes all of that work we just did and converts the GraphQL schema strings it was passed into raw GraphQL types.

Oh well, at least it works.

Coming to Terms

While the createType/printType conversion process works, I wasn’t happy with the solution.

In my throes of desperation, I opened an issue on the graphql-tools project asking for either a better approach to this problem, or support for raw GraphQL types within makeExecutableSchema.

Sashko confirmed my suspicions that there currently isn’t a better way of dealing with this situation. He mentioned that the ability to intermingle these two styles of schema creation within makeExecutableSchema has been requested in the past, and pull requests are welcome.

Maybe I’ll build up the courage to dive deeper into graphql-tools and look for a better solution.