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.
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:
- Node.js and npm installed on your system.
- MongoDB installed locally or access to a MongoDB cloud instance (e.g., MongoDB Atlas).
- 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
.envfile.
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:
dotenvloads the.envvariables.mongoose.connectconnects to the MongoDB database.- The Express server listens on the port specified in
.envor defaults to3000.
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
