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

Building A REST API With MongoDB, Mongoose, And Node.js

TwitterFacebookRedditLinkedInHacker News

About a week or so ago I had written a tutorial titled, Getting Started with MongoDB as a Docker Container Deployment, which focused on the deployment of MongoDB. In that tutorial we saw how to interact with the MongoDB instance using the shell client, but what if we wanted to actually develop a web application with MongoDB as our NoSQL database?

In this tutorial we’re going to see how to develop a REST API with create, retrieve, update, and delete (CRUD) endpoints using Node.js and the very popular Mongoose object document modeler (ODM) to interact with MongoDB.

Before we get too invested in this tutorial, I wanted to point out that the focus of this tutorial won’t be around installing, configuring, or deploying MongoDB instances. I recommend you take a look at my previous tutorial if you need help with that. The assumption is that your MongoDB database is ready to go.

Configuring Node.js with Express Framework

To start things off, let’s go ahead and create a fresh Node.js project with the appropriate dependencies. From the command line, execute the following:

npm init -y
npm install express body-parser mongoose --save

The above commands will create a new package.json file and install Express.js, Mongoose, and a package that will allow us to pass JSON data around in our requests.

For simplicity, we’re going to keep all of our code within a single JavaScript file. Create an app.js file at the root of your project and include the following boilerplate code:

const Express = require("express");
const Mongoose = require("mongoose");
const BodyParser = require("body-parser");

var app = Express();

app.use(BodyParser.json());
app.use(BodyParser.urlencoded({ extended: true }));

app.post("/person", async (request, response) => {});
app.get("/people", async (request, response) => {});
app.get("/person/:id", async (request, response) => {});
app.put("/person/:id", async (request, response) => {});
app.delete("/person/:id", async (request, response) => {});

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

In the above code we are doing a few things. First we are importing each of the dependencies that we had previously downloaded when creating our project. Next we are initializing Express Framework and configuring the body-parser package so we can receive JSON data in our payloads.

The API we develop will be create, retrieve, update, and delete (CRUD) oriented hence the five endpoint functions that are prepared. We’ll be adding the MongoDB and Mongoose logic to each of these endpoint functions.

Finally, we are listening for requests to our application on port 3000.

Interacting with MongoDB using the Mongoose ODM

With the foundation of our REST API in place, we can focus on the database logic. Within the project’s app.js file include the following near where you initialized Express Framework:

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

The above line will connect to our MongoDB instance running on localhost and it will either use or create a thepolyglotdeveloper database. If you followed the Docker example, the database will have already been created. If you’re not using localhost, change it up as necessary.

After the connection information is in place, we can define our document models. This particular example will only have a single document model that looks like the following:

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

Our model is person which will create a people collection within our database. Each document in our collection will have the firstname and lastname properties. The model can be significantly more complex than what we have here.

So let’s take a look at each of our endpoints, starting with the creation of data:

app.post("/person", async (request, response) => {
    try {
        var person = new PersonModel(request.body);
        var result = await person.save();
        response.send(result);
    } catch (error) {
        response.status(500).send(error);
    }
});

When the client makes a POST request to our endpoint, we can use our PersonModel and the JSON payload provided to save into the database. Only the most basic of data validation is happening on our JSON payload and rather than using promises directly, we are using async and await, which in my opinion is a little cleaner. If the save is successful, we return the data back to the client facing application.

Now that we have data in our database, we can try to retrieve it:

app.get("/people", async (request, response) => {
    try {
        var result = await PersonModel.find().exec();
        response.send(result);
    } catch (error) {
        response.status(500).send(error);
    }
});

There are two different retrievals that can happen. We can retrieve everything, or we can retrieve something in particular. The first retrieval that we are looking at is the everything scenario. Using a find with no properties will get all data for a given collection, which in our scenario is the people collection. That data is then returned to the client facing application.

On the other side of things, we can try to retrieve a single document based on its stored id value:

app.get("/person/:id", async (request, response) => {
    try {
        var person = await PersonModel.findById(request.params.id).exec();
        response.send(person);
    } catch (error) {
        response.status(500).send(error);
    }
});

Given an id value defined by the client facing application, we can call the findById function rather than the find function. This function will find a single document based on the associated id. When we have this data we will return it back to the user.

Not bad so far, right?

Let’s finish this simple API with an update and a delete endpoint. Starting with an update, we can do the following:

app.put("/person/:id", async (request, response) => {
    try {
        var person = await PersonModel.findById(request.params.id).exec();
        person.set(request.body);
        var result = await person.save();
        response.send(result);
    } catch (error) {
        response.status(500).send(error);
    }
});

When the client provides an id value, we can first find the document by the id. Once we’ve found the document, we can set the properties that were provided in the request payload. Again, basic validation is performed. For example, if a property is provided that doesn’t appear in our model, it will be stripped out. In our model, none of the properties are required. This means whatever data appears in the payload, as long as it is valid, it will override what already exists. We can save any changes we made back to the database and return the data back to the client.

Our final endpoint for this example is the delete endpoint:

app.delete("/person/:id", async (request, response) => {
    try {
        var result = await PersonModel.deleteOne({ _id: request.params.id }).exec();
        response.send(result);
    } catch (error) {
        response.status(500).send(error);
    }
});

Again, we are expecting an id to be provided from the client facing application. When we have an id, we can use it in the deleteOne function and the document will be removed from the database.

If you run your application, you can play around with it using a tool like Postman or similar.

Conclusion

You just saw how to build a simple RESTful API using popular technologies such as Node.js, JavaScript, and MongoDB as the NoSQL database. If you’ve been following the blog, you might remember I did something similar in a tutorial titled, Developing a RESTful API with Node.js and MongoDB Atlas. With Atlas, we built a REST API, but it was with a cloud deployment of MongoDB.

If you’re interested in learning more about RESTful API development, I encourage you to check out my eBook and video course titled, Web Services for the JavaScript Developer, as it goes into significantly more depth.

A video version of this article can be found 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.