Skip to content

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

· 21 min read

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.

REST API Architecture Overview

graph TB
    subgraph "Client Layer"
        WEB[Web Application]
        MOBILE[Mobile App]
        API_CLIENT[API Client]
    end
    
    subgraph "HTTP Methods"
        GET[GET /users<br/>Read Operations]
        POST[POST /users<br/>Create Operations]
        PUT[PUT /users/:id<br/>Update Operations]
        DELETE[DELETE /users/:id<br/>Delete Operations]
    end
    
    subgraph "Express.js Server"
        ROUTER[Express Router<br/>Route Handling]
        MIDDLEWARE[Middleware<br/>JSON Parser, Auth]
        CONTROLLER[Controllers<br/>Business Logic]
    end
    
    subgraph "Database Layer"
        MONGOOSE[Mongoose ODM<br/>Object Modeling]
        MONGODB[(MongoDB Database<br/>Document Storage)]
    end
    
    subgraph "Data Flow"
        REQUEST[HTTP Request]
        RESPONSE[HTTP Response]
        VALIDATION[Data Validation]
        PERSISTENCE[Data Persistence]
    end
    
    WEB --> GET
    MOBILE --> POST
    API_CLIENT --> PUT
    API_CLIENT --> DELETE
    
    GET --> ROUTER
    POST --> ROUTER
    PUT --> ROUTER
    DELETE --> ROUTER
    
    ROUTER --> MIDDLEWARE
    MIDDLEWARE --> CONTROLLER
    CONTROLLER --> MONGOOSE
    MONGOOSE --> MONGODB
    
    MONGODB --> PERSISTENCE
    PERSISTENCE --> VALIDATION
    VALIDATION --> RESPONSE
    
    style GET fill:#e8f5e8
    style POST fill:#e1f5fe
    style PUT fill:#fff3e0
    style DELETE fill:#ffebee
    style MONGODB fill:#f3e5f5

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:

# @filename: script.sh
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

// @filename: 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:

// @filename: config.js
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.

User Schema Structure

erDiagram
    USER {
        ObjectId _id PK "MongoDB Generated"
        String name "Required, Non-empty"
        String email UK "Required, Unique"
        Number age "Required, Positive Integer"
        Date createdAt "Auto-generated"
        Date updatedAt "Auto-updated"
    }
    
    USER ||--o{ USER_SESSION : has
    USER ||--o{ USER_PROFILE : extends
    
    USER_SESSION {
        ObjectId _id PK
        ObjectId userId FK
        String token
        Date expiresAt
    }
    
    USER_PROFILE {
        ObjectId _id PK
        ObjectId userId FK
        String avatar
        String bio
        Object preferences
    }

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:

// @filename: routes.js
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: Retrieves all users.
  • GET /users/:id: Retrieves a single user by ID.
  • PUT /users/:id: Updates a user by ID.
  • DELETE /users/:id: Deletes a user by ID.

CRUD Operations Flow

sequenceDiagram
    participant Client
    participant Express as Express Router
    participant Mongoose as Mongoose ODM
    participant MongoDB as MongoDB Database
    
    Note over Client,MongoDB: CREATE Operation (POST /users)
    Client->>Express: POST /users {name, email, age}
    Express->>Express: Validate request body
    Express->>Mongoose: new User(data).save()
    Mongoose->>MongoDB: Insert document
    MongoDB-->>Mongoose: Document created with _id
    Mongoose-->>Express: User object with _id
    Express-->>Client: 201 Created + User data
    
    Note over Client,MongoDB: READ Operation (GET /users)
    Client->>Express: GET /users
    Express->>Mongoose: User.find()
    Mongoose->>MongoDB: Query all documents
    MongoDB-->>Mongoose: Array of user documents
    Mongoose-->>Express: Array of User objects
    Express-->>Client: 200 OK + Users array
    
    Note over Client,MongoDB: UPDATE Operation (PUT /users/:id)
    Client->>Express: PUT /users/123 {name: "New Name"}
    Express->>Mongoose: User.findByIdAndUpdate(id, data)
    Mongoose->>MongoDB: Update document by _id
    MongoDB-->>Mongoose: Updated document or null
    Mongoose-->>Express: Updated User object or null
    Express-->>Client: 200 OK + Updated user OR 404 Not Found
    
    Note over Client,MongoDB: DELETE Operation (DELETE /users/:id)
    Client->>Express: DELETE /users/123
    Express->>Mongoose: User.findByIdAndDelete(id)
    Mongoose->>MongoDB: Delete document by _id
    MongoDB-->>Mongoose: Deleted document or null
    Mongoose-->>Express: Deleted User object or null
    Express-->>Client: 200 OK + Success message OR 404 Not Found
  • GET /users
Share: