Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Developing A GraphQL API With Node.js And MongoDB

TwitterFacebookRedditLinkedInHacker News

While REST APIs are amongst the most popular when it comes to client consumption, they are not the only way to consume data and they aren’t always the best way. For example, having to deal with many endpoints or endpoints that return massive amounts of data that you don’t need are common. This is where GraphQL comes in.

With GraphQL you can query your API in the same sense that you would query a database. You write a query, define the data you want returned, and you get what you requested. Nothing more, nothing less. I actually had the opportunity to interview the co-creator of GraphQL on my podcast in an episode titled, GraphQL for API Development, and in that episode we discuss GraphQL at a high level.

You might remember that I wrote a tutorial titled, Getting Started with GraphQL Development Using Node.js which focused on mock data and no database. This time around we’re going to take a look at including MongoDB as our NoSQL data layer.

A few assumptions are going to be made going forward. I’m going to assume that you already have access to a MongoDB instance. If you don’t and need help getting MongoDB setup, you might check out my tutorial titled, Getting Started with MongoDB as a Docker Container Deployment. The other assumption is that you have Node.js installed and configured. While this tutorial will be a working example, if you want to get into more depth with GraphQL, I suggest you check out my eBook and video course titled Web Services for the JavaScript Developer.

Creating a New Node.js Project for GraphQL Development

The first step in this tutorial will be to create a new project with the dependencies and boilerplate GraphQL code. From the command line, execute the following commands:

npm init -y
npm install express express-graphql graphql mongoose --save

The above commands will create a new package.json file and install our dependencies. The backbone of this project will use Express.js, which is also commonly used for RESTful APIs. However, we’ll be processing GraphQL requests and connecting them to MongoDB with the Mongoose ODM.

The next step is to create an app.js file and include the following boilerplate JavaScript code:

const Express = require("express");
const ExpressGraphQL = require("express-graphql");
const Mongoose = require("mongoose");
const {
    GraphQLID,
    GraphQLString,
    GraphQLList,
    GraphQLNonNull,
    GraphQLObjectType,
    GraphQLSchema
} = require("graphql");

var app = Express();

Mongoose.connect("mongodb://localhost/thepolyglotdeveloper");

const PersonModel = Mongoose.model("person", {
    firstname: String,
    lastname: String
});

const PersonType = new GraphQLObjectType({
    name: "Person",
    fields: {
        id: { type: GraphQLID },
        firstname: { type: GraphQLString },
        lastname: { type: GraphQLString }
    }
});

const schema = new GraphQLSchema({});

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

app.listen(3000, () => {
    console.log("Listening at :3000...");
});

In the above code we are importing our dependencies, initializing Express.js, and connecting to our MongoDB instance with Mongoose. We’re planning to use a very simple Mongoose model which will also be seen in our GraphQL model.

Rather than reiterating the content that was discussed in the previous tutorial, we’re going to focus primarily on the schema in our project which will contain queries and mutations.

Designing GraphQL Queries for Retrieving MongoDB NoSQL Data

When it comes to our schema, there will be queries for retrieving data and mutations for creating, updating, or deleting data. We’re going to start by designing our queries which will retrieve data from MongoDB.

Take a look at the following queries:

const schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: "Query",
        fields: {
            people: {
                type: GraphQLList(PersonType),
                resolve: (root, args, context, info) => {
                    return PersonModel.find().exec();
                }
            },
            person: {
                type: PersonType,
                args: {
                    id: { type: GraphQLNonNull(GraphQLID) }
                },
                resolve: (root, args, context, info) => {
                    return PersonModel.findById(args.id).exec();
                }
            }
        }
    })
});

In the above code we have a people query as well as a person query. One will retrieve multiple documents and the other will retrieve a single document. When we wish to query for multiple documents, we specify we want to return a GraphQLList of the PersonType that we had created. This PersonType maps to our document model. We can simply do a find for all documents within our people MongoDB collection.

The person query is similar, but not quite the same. In the person query we accept an argument which must be present. With that argument we can use the findById function and return the result.

We didn’t need to specify the document properties because they are already conveniently mapped to our model because of our naming conventions. If we wanted to use different names, we could specify the properties in the find operation.

If we wanted to query for our data from a client facing application, we could run something like this:

{
    people {
        id,
        firstname,
        lastname
    }
    person(id: "123") {
        firstname
    }
}

You can try to run this query by navigating to http://localhost:3000/graphql in your web browser because we have GraphiQL enabled for troubleshooting.

Designing GraphQL Mutations for Creating, Changing, or Deleting MongoDB Documents

Now that we can query for documents, we probably want a way to create documents. Mutations are designed nearly the same as queries, but they are executed differently in the client facing application.

Within your schema we can modify it to the following:

const schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: "Query",
        fields: {
            people: {
                type: GraphQLList(PersonType),
                resolve: (root, args, context, info) => {
                    return PersonModel.find().exec();
                }
            },
            person: {
                type: PersonType,
                args: {
                    id: { type: GraphQLNonNull(GraphQLID) }
                },
                resolve: (root, args, context, info) => {
                    return PersonModel.findById(args.id).exec();
                }
            }
        }
    }),
    mutation: new GraphQLObjectType({
        name: "Mutation",
        fields: {
            person: {
                type: PersonType,
                args: {
                    firstname: { type: GraphQLNonNull(GraphQLString) },
                    lastname: { type: GraphQLNonNull(GraphQLString) }
                },
                resolve: (root, args, context, info) => {
                    var person = new PersonModel(args);
                    return person.save();
                }
            }
        }
    })
});

Notice that now we have a mutation and not just a query. For the mutation, we are requiring two arguments to exist and we are using them in the resolve function. Using the arguments, without further data validation, we create a new model instance and save it to the database. The results are returned to the client that executed the mutation.

To try this mutation, the following could be executed:

mutation CreatePerson($firstname: String!, $lastname: String!) {
    person(firstname: $firstname, lastname: $lastname) {
        id,
        firstname,
        lastname
    }
}

The above code would make sense in GraphiQL. The variables would be populated with another part of the GraphiQL application. Various frameworks will have different expectations when it comes to executing mutations, but the same idea applies.

Conclusion

You just saw how to use MongoDB as the NoSQL database in your GraphQL web application. GraphQL can be used in combination with REST or as an alternative depending on your business needs. If you’d like to know how to create a RESTful application with MongoDB, you might want to check out my previous tutorial titled, Building a REST API with MongoDB, Mongoose, and Node.js.

This example was quite simple and a lot more can be accomplished with GraphQL. If you’d like to learn more, I encourage you to check out my video course and eBook titled, Web Services for the JavaScript Developer.

A video version of this tutorial can be seen below.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.