Building RESTful APIs with Node.js and Express.js

Learn to build RESTful APIs using Node.js and Express.js. This guide covers the core concepts of creating, testing, and deploying APIs.

S

StalkTechie

Author

September 29, 2025
1199 views

Building RESTful APIs with Node.js and Express.js

Node.js and Express.js provide a fast, minimal, and scalable framework for building RESTful APIs. This guide walks you through creating APIs, structuring routes, handling requests, and optimizing for performance and security in real-world applications.

Introduction to RESTful APIs

A RESTful API follows the principles of Representational State Transfer (REST). It allows clients to interact with server resources via HTTP methods, keeping operations stateless and standardized.

  • GET – Retrieve data
  • POST – Create new resources
  • PUT – Update existing resources
  • DELETE – Remove resources

Setting Up Node.js and Express


# Initialize a new Node.js project
mkdir node-api-example
cd node-api-example
npm init -y

# Install dependencies
npm install express dotenv mongoose cors
        

// Basic server setup using Express
const express = require("express");
const app = express();

// Middleware
app.use(express.json());
app.use(require("cors")());

// Basic route
app.get("/", (req, res) => {
    res.send("Welcome to the Node.js RESTful API!");
});

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

Creating Modular Routes

It’s best practice to keep routes modular by separating logic into controllers and routes.


// routes/userRoutes.js
const express = require("express");
const router = express.Router();
const userController = require("../controllers/userController");

router.get("/", userController.getAllUsers);
router.post("/", userController.createUser);
router.get("/:id", userController.getUserById);
router.put("/:id", userController.updateUser);
router.delete("/:id", userController.deleteUser);

module.exports = router;
        

// controllers/userController.js
const User = require("../models/User");

exports.getAllUsers = async (req, res) => {
    try {
        const users = await User.find();
        res.json(users);
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
};

exports.createUser = async (req, res) => {
    const user = new User(req.body);
    try {
        const savedUser = await user.save();
        res.status(201).json(savedUser);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
};
        

Integrating MongoDB with Mongoose


// config/db.js
const mongoose = require("mongoose");
mongoose.connect(process.env.MONGO_URI)
    .then(() => console.log("MongoDB connected"))
    .catch(err => console.error(err));

// models/User.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
    name: String,
    email: String,
    age: Number
});
module.exports = mongoose.model("User", userSchema);
        

Adding Middleware for Error Handling


// middleware/errorHandler.js
module.exports = (err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ message: "Something went wrong!" });
};

// In app.js
const errorHandler = require("./middleware/errorHandler");
app.use(errorHandler);
        

Securing the API

APIs are often exposed publicly, so securing them is essential.

  • Use environment variables for secrets with dotenv
  • Enable CORS for specific domains
  • Validate data to prevent injection attacks
  • Protect routes with JWT authentication

// Example JWT authentication middleware
const jwt = require("jsonwebtoken");

function authenticateToken(req, res, next) {
    const token = req.headers["authorization"]?.split(" ")[1];
    if (!token) return res.sendStatus(401);
    
    jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
        if (err) return res.sendStatus(403);
        req.user = user;
        next();
    });
}
        

Optimizing Performance

  • Enable gzip compression using compression middleware
  • Cache frequent responses with Redis
  • Use helmet for enhanced security headers
  • Minimize database calls using indexing and lean queries

// Example using compression and helmet
const compression = require("compression");
const helmet = require("helmet");

app.use(helmet());
app.use(compression());
        

Testing Your API

You can test API endpoints with tools like Postman or automated tools like Jest and Supertest.


// Example test using Jest and Supertest
const request = require("supertest");
const app = require("../app");

describe("GET /users", () => {
    it("should return an array of users", async () => {
        const res = await request(app).get("/users");
        expect(res.statusCode).toBe(200);
        expect(Array.isArray(res.body)).toBe(true);
    });
});
        

Best Practice Summary

  • Organize your routes and controllers clearly
  • Use environment variables for sensitive data
  • Always include error handling middleware
  • Secure your endpoints with authentication
  • Optimize performance with caching and compression
Share this post:

Related Articles

Discussion

0 comments

Please log in to join the discussion.

Login to Comment