Building a REST API with Node.js and MongoDB: A Step-by-Step Guide


Building a REST API with Node.js and MongoDB: A Step-by-Step Guide

Building a REST API is essential for modern web applications, enabling you to interact with your backend using standard HTTP methods. In this guide, we’ll build a REST API with Node.js, Express, and MongoDB, covering everything from setting up the project to implementing CRUD operations. By the end, you’ll have a fully functional API that you can use as a backend for web or mobile applications.


Prerequisites

To follow along with this tutorial, you’ll need:

  1. Node.js and npm installed on your system.
  2. MongoDB installed locally or access to a MongoDB cloud instance (e.g., MongoDB Atlas).
  3. Basic understanding of JavaScript, Node.js, and REST APIs.

Project Setup

First, let’s set up a new Node.js project and install the necessary dependencies.

Step 1: Create a New Project

Open a terminal, create a new directory, and initialize a Node.js project:

mkdir rest-api
cd rest-api
npm init -y

Step 2: Install Dependencies

We’ll use Express for the web server and Mongoose for connecting to MongoDB.

npm install express mongoose dotenv
  • express: A minimalist web framework for building REST APIs.
  • mongoose: An ODM (Object Data Modeling) library for MongoDB, which simplifies database interactions.
  • dotenv: Loads environment variables from a .env file.

Configuring MongoDB Connection

We’ll store our MongoDB connection string in an .env file to keep it secure.

Step 1: Create a .env File

In the project root, create a .env file and add your MongoDB URI:

MONGODB_URI=mongodb://localhost:27017/rest_api
PORT=5000

Replace the MONGODB_URI value with your actual MongoDB connection string.

Step 2: Set Up the Database Connection

In the root directory, create a new file named server.js. This file will be the entry point for our application.

server.js

require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");

const app = express();

// Middleware
app.use(express.json());

// Connect to MongoDB
mongoose
  .connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log("Connected to MongoDB"))
  .catch((error) => console.error("MongoDB connection error:", error));

// Start the server
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

In this setup:

  • dotenv loads the .env variables.
  • mongoose.connect connects to the MongoDB database.
  • The Express server listens on the port specified in .env or defaults to 3000.

Run the server to test the connection:

node server.js

If the connection is successful, you’ll see the message "Connected to MongoDB" in the terminal.


Defining the Data Model with Mongoose

Let’s create a simple data model for a User with fields for name, email, and age.

Step 1: Create a models Directory

Inside the project directory, create a models folder and a file named User.js.

Step 2: Define the User Model

In models/User.js, define the User schema:

const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  age: {
    type: Number,
    required: true,
  },
});

module.exports = mongoose.model("User", userSchema);

This schema defines the structure of a user document with three fields: name, email, and age. Each field has a type and a validation rule.


Creating CRUD Routes for the API

Now, let’s create CRUD routes to manage users. We’ll define routes for creating, reading, updating, and deleting users.

Step 1: Create a routes Directory

Inside the project directory, create a routes folder and a file named users.js.

Step 2: Define User Routes

In routes/users.js, define routes for each CRUD operation:

const express = require("express");
const router = express.Router();
const User = require("../models/User");

// CREATE a new user
router.post("/", async (req, res) => {
  try {
    const user = new User(req.body);
    await user.save();
    res.status(201).json(user);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// READ all users
router.get("/", async (req, res) => {
  try {
    const users = await User.find();
    res.json(users);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// READ a single user by ID
router.get("/:id", async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) return res.status(404).json({ message: "User not found" });
    res.json(user);
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

// UPDATE a user by ID
router.put("/:id", async (req, res) => {
  try {
    const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
    if (!user) return res.status(404).json({ message: "User not found" });
    res.json(user);
  } catch (error) {
    res.status(400).json({ message: error.message });
  }
});

// DELETE a user by ID
router.delete("/:id", async (req, res) => {
  try {
    const user = await User.findByIdAndDelete(req.params.id);
    if (!user) return res.status(404).json({ message: "User not found" });
    res.json({ message: "User deleted" });
  } catch (error) {
    res.status(500).json({ message: error.message });
  }
});

module.exports = router;

Each route performs a CRUD operation:

  • POST /users: Creates a new user.
  • GET /users