Skip links

How to Build a RESTful API with Node.js and Express: A Step-by-Step Guide

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 or express-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 or pino 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.

Resources

This website uses cookies to improve your web experience.