Building RESTful APIs with Node.js & Express

Introduction

Learn to build robust REST APIs with Node.js and Express, covering routing, middleware, error handling, and security essentials for maintainable apps.

Written At

2025-06-01

Updated At

2025-06-01

Reading time

14 minutes

Step 1: Project Setup & Basic Server

Why it matters: Proper structure from the beginning prevents technical debt.

What to do:

  1. Initialize project and install dependencies:
    bash
    npm init -y
    npm install express body-parser cors
    npm install --save-dev nodemon
  2. Create basic server:
    javascript
    const express = require('express');
    const app = express();
    
    app.use(express.json());
    
    app.get('/', (req, res) => {
      res.json({ message: 'API is running' });
    });
    
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => {
      console.log(`Server running on port ${PORT}`);
    });

Example:

Folder structure for scalability:

bash
project/
├── src/
│   ├── controllers/
│   ├── routes/
│   ├── models/
│   ├── middleware/
│   └── app.js
├── .env
└── package.json

Step 2: Route Handling & Controllers

Why it matters: Separation of concerns makes your code more maintainable.

What to do:

  1. Create modular routes:
    javascript
    // routes/users.js
    const express = require('express');
    const router = express.Router();
    const { getUsers, createUser } = require('../controllers/users');
    
    router.route('/')
      .get(getUsers)
      .post(createUser);
    
    module.exports = router;
  2. Implement controller logic:
    javascript
    // controllers/users.js
    const getUsers = async (req, res) => {
      try {
        const users = await User.find({});
        res.status(200).json(users);
      } catch (error) {
        res.status(500).json({ message: error.message });
      }
    };

Example:

Registering routes in main app:

javascript
// app.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);

Step 3: Error Handling & Security

Why it matters: Proper error handling prevents crashes and security vulnerabilities.

What to do:

  1. Implement error middleware:
    javascript
    // middleware/errorHandler.js
    const errorHandler = (err, req, res, next) => {
      const statusCode = err.statusCode || 500;
      res.status(statusCode).json({
        message: err.message,
        stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack
      });
    };
  2. Add security middleware:
    javascript
    app.use(helmet()); // Sets various HTTP headers
    app.use(cors({
      origin: process.env.CLIENT_URL
    }));
    app.use(rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100 // limit each IP to 100 requests per window
    }));

Example:

Custom error class for consistent errors:

javascript
class ErrorResponse extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
  }
}

// Usage in controller
throw new ErrorResponse('User not found', 404);

Conclusion:

Building REST APIs with Node.js and Express combines simplicity with powerful capabilities. Remember to structure your project properly, handle errors gracefully, and implement security measures. For production, consider adding request validation with express-validator and logging with winston.

Related Blogs