9 Steps to Build a RESTful API with Node.js, MongoDB, and Express

In this post, I’ll guide you through the process to build a RESTful API using Node.js, MongoDB, and Express.

Before starting, I’m assuming that you have Node.js, npm, MongoDB, and VS Code installed on your machine.

If you haven’t yet, then you can check the following:

Let’s jump right into it!๐Ÿš€

Step 1: Setting Up the Project to Build a RESTful API

Let’s create a new project folder to get started. Open your terminal and run the following commands one by one.

mkdir my-first-restful-api
cd my-first-restful-api

This will create a new directory on your machine.

RESTful API with Node.js, MongoDB, and Express

Now, let’s initialize our project by running the following command in the terminal (make sure you’re in your newly created project folder).

npm init

The above command will walk you through creating a package.json file. Enter the details for the query and hit Enter to go to the next query.

Or you can run the below command to create a package.json file with default settings.

npm init -y

In the image below, I’ve used npm init command.

RESTful API with Node.js, MongoDB, and Express

After entering all the details, a file named package.json will be created in your project folder, as you can see in the image below.

package.json

Step 2: Installing Dependencies

Now, let’s install the necessary packages for our project by running the following command in the terminal.

npm install express mongoose dotenv

The above command will install

  • Express: For building the web server.
  • Mongoose: An ODM (Object Data Modeling) library for MongoDB.
  • dotenv: For loading environment variables from a .env file.
RESTful API

After running the above command, this should be the structure of your project folder.

RESTful API with Node.js, MongoDB, and Express

What is the .env file, and why do we need it?

A .env file is used to store configuration variables, such as API keys, database credentials, and other sensitive information, separately from the codebase for security purposes.

Note: If you push your code to GitHub, then make sure you don’t push .env file, so that no one can access your sensitive information. You can do so by adding .env in your .gitignore file.

Step 3: Set Up a Basic Express app

Create a file namedindex.js in your root directory.

Node.js, MongoDB, and Express

Now, add the following code to the index.js file.

const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(port, () => {
  console.log(`Server is listening on port ${port}`);
});

You might be wondering, do I have to remember the above code, or where this code has come from?๐Ÿค”

Let me tell you where you can find this code.

  • Go to expressjs.com
  • Hover over Getting started and click on Hello world.
  • You’ll find the code for the basic Hello World example.
Express JS

Now, let’s run our server with the following command:

node index.js

(For this command, I’ve used VS Code’s built-in terminal. You can use your machine’s terminal as well.)

This should be the output in your terminal.

node index.js

In your browser, if you go to localhost:3000 then you’ll see Hello world! on the page as follows.

Express Server

So, our server is running properly.

Step 4: Creating a MongoDB Database

In this step, we will create a Database on MongoDB Atlas.

  • Go to MongoDB Atlas and create an account or sign in if you already have one.
  • If you’re creating a new account, then it will ask you to create a username and password, so create those. Then, in the second step, choose “My Local Environment” and fill in your IP address. After that, click on “Finish and Close”.
MongoDB Database
  • Then go to Database (You’ll find it on the left menu) and click on the Create button (on the right side of the screen). Create a Free Shared Cluster.
  • Now, on the home page of the cluster, click on the connect button.
MongoDB

The following window will appear, and then click on the Compass link.

MongoDB Cluster

Copy the connection string and replace <password> with your password, which you’ve created before. Also, install the MongoDB Compass if you haven’t yet.

Your connection string should look like this.

mongodb+srv://shefali:********@cluster0.sscvg.mongodb.net/

In your connection string, instead of “shefali”, this will display your username, and instead of “********”, your password.

Step 5: Creating a .env file

To protect our MongoDB connection string, let’s create a new file named .env in the root directory.

RESTful API structure

Add the following code to the .env file.

MONGODB_URL=<Your MongoDB Connection String>

Replace <Your MongoDB Connection String> with the connection string you obtained from MongoDB Atlas. Now your .env file should look like this.

MONGODB_URL=mongodb+srv://shefali:********@cluster0.sscvg.mongodb.net/my-first-restful-api

In the above string, at last I’ve added my-first-restful-api which is our database name.

Step 6: Connecting to MongoDB using Mongoose

Add the following code to your index.js file to connect to the MongoDB.

const mongoose = require('mongoose');
require('dotenv').config(); //for using variables from .env file.

mongoose.connect(process.env.MONGODB_URL).then(() => {
  console.log("MongoDB is connected!");
});

Here process.env.MONGODB_URL will get the connection string from the .env file.

Your final code should look like the following:

const express = require("express");
const mongoose = require("mongoose");

require("dotenv").config(); //for using variables from .env file.

const app = express();
const port = 3000;

mongoose.connect(process.env.MONGODB_URL).then(() => {
  console.log("MongoDB is connected!");
});

app.get("/", (req, res) => {
  res.send("Hello World!");
});

app.listen(port, () => {
  console.log(`Server is listening on port ${port}`);
});

Now, when you restart your server using node index.js command, your terminal output should be like this.

node index.js

Note: If you don’t see any changes, then make sure to restart your server.

In this step, we have successfully connected to MongoDB using mongoose.

Step 7: Defining a simple schema

Let’s create a simple schema for our project. (Know more about Schemas)

For this, create a new folder named model in your root directory and inside that folder create a file named todo.js.

MongoDB Schema

Add the following code to the todo.js file.

const mongoose = require("mongoose");
const { Schema } = mongoose;

const todoSchema = new Schema({
  title: {
    type: String
  },
  description: {
    type: String
  },
});

const Todo = mongoose.model("Todo", todoSchema);
module.exports = Todo;

And import this model in your index.js file like this.

const Todo = require("./model/todo");

Step 8: Implementing CRUD Operations

Let’s start creating routes for basic CRUD operations.

Create

Add the following code to the index.js file.

// Create an todo item
app.post("/todos/create", async (req, res) => {
  try {
    const newTodo = await Todo.create(req.body);
    res.status(201).json(newTodo);
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

In the above code,

  • There’s an HTTP POST endpoint /todos/create for creating a new todo item.
  • When a POST request is made to /todos/create, the code inside the async function is executed.
  • It uses the Todo.create method (All methods for models) to create a new todo item based on the data in the request body (req.body).
  • If the creation is successful, it responds with a status code 201 and sends the newly created todo item in JSON format as a response.
  • If there’s an error during the process, it catches the error and responds with a status code 500 and error message Internal Server Error.

In simpler terms, this code handles requests to create a new todo item. It tries to create the item, and if successful, it responds with the created item. If there’s an issue during the process, it responds with an error message.

Read

Replace the code of app.get to the following code in the index.js file.

// Read all todos
app.get("/todos", async (req, res) => {
  try {
    const todos = await Todo.find();
    res.status(200).json(todos);
  } catch (error) {
    console.log(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

In the above code,

  • There’s an HTTP GET endpoint /todos for getting the todo items.
  • When a GET request is made to /todos, the code inside the async function is executed.
  • It uses the Todo.find method (All methods for models) to fetch all the todo items from the database.
  • If the fetching is successful, it responds with a status code 200 and sends all the todo items in JSON format as a response.
  • If there’s an error during the process, it catches the error and responds with a status code 500 and error message Internal Server Error.

Update

Add the following code to the index.js file.

// Update a todo by ID
app.put("/todos/:id", async (req, res) => {
  try {
    const updatedTodo = await Todo.findByIdAndUpdate(req.params.id, req.body, {
      new: true,
    });
    res.status(200).json(updatedTodo);
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

In the above code,

  • There’s an HTTP PUT endpoint /todos/:id, where :id is a placeholder for a specific todo item’s id.
  • When a PUT request is made, it tries to update a todo in the database with the specified ID (req.params.id) using the Todo.findByIdAndUpdate method (All methods for models).
  • The new data for the todo is expected to be in the request body (req.body). This data is sent by the client making the PUT request.
  • The third argument { new: true } ensures that the method returns the updated todo after the update operation.
  • If the update is successful, it responds with a status 200 and sends the updated todo in JSON format in the response body.
  • If any error occurs during the update process (for example, if the specified ID is not found), it catches the error and responds with status 500 and error message Internal Server Error.

Delete

Add the following code to the index.js file.

// Delete a todo by ID
app.delete("/todos/:id", async (req, res) => {
  try {
    await Todo.findByIdAndDelete(req.params.id);
    res.status(204).send();
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

In the above code,

  • There’s an HTTP DELETE endpoint /todos/:id, where :id is a placeholder for a specific todo item’s id.
  • When a DELETE request is made, it tries to delete a todo in the database with the specified ID (req.params.id) using the Todo.findByIdAndDelete method (All methods for models).
  • If the deletion is successful, it responds with a status 204(No Content).
  • If any error occurs during the deleting process (for example, if the specified ID is not found), it catches the error and responds with status 500 and error message Internal Server Error.

Now, before testing our API, we need to add the following line of code to our index.js file, because we will read the data in JSON format.

//middleware provided by Express to parse incoming JSON requests.
app.use(express.json()); 

Now, your index.js file should look like this.

const express = require("express");
const mongoose = require("mongoose");
const Todo = require("./model/todo");

require("dotenv").config(); //for using variables from .env file.

const app = express();
const port = 3000;

//middleware provided by Express to parse incoming JSON requests.
app.use(express.json());

mongoose.connect(process.env.MONGODB_URL).then(() => {
  console.log("MongoDB is connected!");
});

// Create an todo item
app.post("/todos/create", async (req, res) => {
  try {
    const newTodo = await Todo.create(req.body);
    res.status(201).json(newTodo);
  } catch (error) {
    console.log(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// Read all todos
app.get("/todos", async (req, res) => {
  try {
    const todos = await Todo.find();
    res.status(200).json(todos);
  } catch (error) {
    console.log(error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// Update a todo by ID
app.put("/todos/:id", async (req, res) => {
  try {
    const updatedTodo = await Todo.findByIdAndUpdate(req.params.id, req.body, {
      new: true,
    });
    res.status(200).json(updatedTodo);
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// Delete a todo by ID
app.delete("/todos/:id", async (req, res) => {
  try {
    await Todo.findByIdAndDelete(req.params.id);
    res.status(204).send();
  } catch (error) {
    res.status(500).json({ error: "Internal Server Error" });
  }
});

app.listen(port, () => {
  console.log(`Server is listening on port ${port}`);
});

Step 9: Testing the API

By using the command node index.js, we have to restart our server each time. To avoid this, you can install nodemon using the following command. This restarts the server automatically each time you make changes to your file.

npm install -g nodemon

Now run your server using the following command.

nodemon index.js
nodemon index.js

For testing the API, I’m using the VS Code extension named Thunder Client. You can use Postman as well.

Let’s test our API now.

Create a new todo item using a POST request

In the Headers tab, add Content-Type to application/json, choose POST and write the URL like the image below.

CRUD RESTful API

In the Body tab, add a title and description of the todo item as shown in the image.

CRUD RESTful API

After that, click on the Send button, and you’ll get the following response.

CRUD RESTful API

We have successfully created our first todo item. You can create more todo items with different titles and descriptions using the same process.

You can also see these items in your MongoDB compass app as well after connecting with your connection string, like the image below.

MongoDB Compass

Get all todos using a GET request

To get the todos, choose GET and write the URL like the image below, and click on the Send button.

CRUD RESTful API

You’ll get the response as shown in the image.

CRUD RESTful API

Update a todo item using a PUT request

For updating the todo, we will need the id of the todo item. So copy the id of the todo that you got in the previous response.

Choose PUT and write the URL as shown in the image.

CRUD RESTful API

Now, in the Body tab, update the title and description.

CRUD RESTful API

Click on the Send button and you’ll get the following response.

CRUD RESTful API

Delete a todo item using a DELETE request

For deleting a todo item, choose DELETE and write the URL as shown in the image.

CRUD RESTful API

After clicking the Send button, you’ll get the following response.

CRUD RESTful API

Our todo item has been deleted.

Congratulations! ๐ŸŽ‰

You’ve successfully created a RESTful API using Node.js, MongoDB, and Express.


Thatโ€™s all for today!

If youโ€™re new to web development, check out Learnify โ€” my curated platform with beginner-friendly tutorials to help you learn web development step-by-step with examples and simple explanations.

If you enjoy my work and want to support what I do:

๐Ÿ‘‰ Become a Patreon supporter
๐Ÿ‘‰ Or buy me a coffee

Every small gesture keeps me going! ๐Ÿ’›

Follow me on X (Twitter) to get daily web development tips & insights.


Enjoyed reading? You may also find these articles helpful.

13 Most Common HTTP Status Codes You Should Know As A Developer

5 Easy Steps to Deploy Your Node.js + MongoDB App to Vercel

5 thoughts on “9 Steps to Build a RESTful API with Node.js, MongoDB, and Express”

  1. Great write-up on building a RESTful API! Thanks for breaking it down into such clear steps. One thing that’s been on my mind: when you’re defining the schema in Mongoose, do you have recommendations for handling validation? I’m curious if there are best practices or common pitfalls to avoid.

    I stumbled upon another blog covering similar topics on https://sebbie.pl/tag/javascript/. It’s got some decent content on Node.js and JavaScript, which might complement some of the insights here.

    Again, appreciate the thorough explanation. Looking forward to trying it out myself soon!

    Reply
  2. certainly like your web site but you have to check the spelling on quite a few of your posts. Many of them are rife with spelling issues and I in finding it very troublesome to inform the truth nevertheless I will certainly come again again.

    Reply

Leave a Comment