Building a RESTful API is one of the most common tasks for modern web developers. Whether you’re building a backend for a mobile app, a web application, or a microservice, using Node.js and Express gives you a powerful, lightweight, and scalable foundation.
In this step-by-step guide, we’ll walk through everything you need to know to create a fully functional RESTful API using Node.js, Express, and MongoDB (with Mongoose). By the end of this tutorial, you’ll have a working API that supports CRUD operations—Create, Read, Update, and Delete.
Prerequisites
Before diving into the code, make sure you have the following installed:
- Node.js (LTS version recommended)
- npm (comes with Node.js)
- MongoDB Atlas account (or local MongoDB instance)
- Postman or Insomnia (for testing your API)
Also, basic knowledge of JavaScript and HTTP methods (GET, POST, PUT, DELETE) will help you follow along more easily.
Setting Up Your Project
Step 1: Initialize a New Node.js Project
Open your terminal and run the following commands:
mkdir rest-api
cd rest-api
npm init -y
This creates a new directory called rest-api
and initializes it with a default package.json
.
Step 2: Install Required Packages
Install the necessary dependencies:
npm install express mongoose dotenv cors helmet morgan
- express: The core framework for building the API
- mongoose: ODM library for interacting with MongoDB
- dotenv: Load environment variables from
.env
file - cors: Enable Cross-Origin Resource Sharing
- helmet: Secure Express apps with HTTP headers
- morgan: HTTP request logger middleware
Optionally, install nodemon
for development:
npm install --save-dev nodemon
Update your package.json
scripts section:
{
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js"
}
}
Creating the Server
Create an index.js
file at the root of your project:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
const app = express();
// Middleware
app.use(express.json());
app.use(cors());
app.use(helmet());
app.use(morgan('dev'));
// Routes
app.get('/', (req, res) => {
res.json({ message: 'Welcome to the RESTful API!' });
});
// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Now start your server:
npm run dev
Visit http://localhost:5000
in your browser or use Postman to see the JSON response.
Connecting to MongoDB
Step 1: Set Up MongoDB Atlas
Sign up for a free cluster at MongoDB Atlas, then get your connection string.
Add it to a .env
file:
MONGO_URI=mongodb+srv://:@cluster0.example.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
PORT=5000
Step 2: Connect Using Mongoose
Update index.js
to connect to MongoDB:
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error('MongoDB connection error:', err));
You should now see “MongoDB connected” in the console when you restart the server.
Define a Mongoose Model
Let’s create a simple model for a collection of products.
Create a folder called models
and add a file named Product.js
:
const mongoose = require('mongoose');
const ProductSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: String,
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Product', ProductSchema);
Create API Routes
Step 1: Create a Controller File
Create a controllers/productController.js
file:
const Product = require('../models/Product');
// Get all products
exports.getProducts = async (req, res) => {
try {
const products = await Product.find();
res.status(200).json(products);
} catch (err) {
res.status(500).json({ error: err.message });
}
};
// Get single product by ID
exports.getProductById = async (req, res) => {
try {
const product = await Product.findById(req.params.id);
if (!product) return res.status(404).json({ message: 'Product not found' });
res.status(200).json(product);
} catch (err) {
res.status(500).json({ error: err.message });
}
};
// Create a new product
exports.createProduct = async (req, res) => {
try {
const product = await Product.create(req.body);
res.status(201).json(product);
} catch (err) {
res.status(400).json({ error: err.message });
}
};
// Update a product
exports.updateProduct = async (req, res) => {
try {
const product = await Product.findByIdAndUpdate(req.params.id, req.body, { new: true });
if (!product) return res.status(404).json({ message: 'Product not found' });
res.status(200).json(product);
} catch (err) {
res.status(400).json({ error: err.message });
}
};
// Delete a product
exports.deleteProduct = async (req, res) => {
try {
const product = await Product.findByIdAndDelete(req.params.id);
if (!product) return res.status(404).json({ message: 'Product not found' });
res.status(200).json({ message: 'Product deleted successfully' });
} catch (err) {
res.status(500).json({ error: err.message });
}
};
Step 2: Define Routes
Create a routes/productRoutes.js
file:
const express = require('express');
const router = express.Router();
const productController = require('../controllers/productController');
router.route('/')
.get(productController.getProducts)
.post(productController.createProduct);
router.route('/:id')
.get(productController.getProductById)
.put(productController.updateProduct)
.delete(productController.deleteProduct);
module.exports = router;
Step 3: Register Routes in index.js
Update your index.js
to include the routes:
const productRoutes = require('./routes/productRoutes');
app.use('/api/products', productRoutes);
Test Your API
Use Postman or Insomnia to test each endpoint:
Method | Endpoint | Description |
---|---|---|
GET | /api/products | Get all products |
GET | /api/products/:id | Get product by ID |
POST | /api/products | Create a new product |
PUT | /api/products/:id | Update a product |
DELETE | /api/products/:id | Delete a product |
Try adding a few sample products via POST requests like:
{
"name": "Wireless Headphones",
"price": 99.99,
"description": "High-quality wireless headphones with noise cancellation."
}
Final Tips & Best Practices
- Validation: Use packages like
joi
orexpress-validator
to validate request data. - Error Handling: Centralize error handling with middleware.
- Authentication: Add JWT-based authentication for secure APIs.
- Pagination & Filtering: Implement query parameters for large datasets.
- Rate Limiting: Use
express-rate-limit
to protect against abuse. - Logging: Use
winston
orpino
for structured logging. - Environment Variables: Always keep sensitive data in
.env
files.
Project Structure Summary
Here’s how your final project structure should look:
rest-api/
├── controllers/
│ └── productController.js
├── models/
│ └── Product.js
├── routes/
│ └── productRoutes.js
├── .env
├── index.js
├── package.json
└── README.md
Conclusion
Congratulations! You’ve just built a full RESTful API using Node.js, Express, and MongoDB. This setup provides a solid foundation for expanding into more complex features like authentication, file uploads, search filters, and real-time updates.
Whether you’re building an MVP, learning backend development, or preparing for interviews, mastering REST API creation is a valuable skill—and now you’ve got the tools to do it confidently.