Node.js – Express.js Middleware

Middleware is one of the most important concepts in Express.js. A middleware function is a function that has access to the request object (req), the response object (res), and a special next function. Middleware sits between the incoming request and the final route handler — it can inspect, modify, or reject a request before it reaches its destination.

Think of middleware like a series of security checkpoints at an airport. A passenger (request) passes through check-in, then baggage screening, then passport control, and finally boards the plane (reaches the route handler). Each checkpoint can stop the passenger or let them continue. Middleware works the same way.

What Can Middleware Do?

  • Execute any code before the request reaches its route handler.
  • Modify the request and response objects (e.g., attach user data).
  • End the request-response cycle (e.g., reject unauthorized requests).
  • Call the next() function to pass control to the next middleware or route.

Basic Middleware Structure

function myMiddleware(req, res, next) {
  console.log("Middleware ran for:", req.method, req.url);
  next(); // Must call next() to continue to the next handler
}

If next() is not called, the request will hang and the client will never receive a response.

Types of Middleware in Express

  • Application-level middleware: Applied to the entire app using app.use().
  • Router-level middleware: Applied to a specific router using router.use().
  • Built-in middleware: Provided by Express itself (e.g., express.json(), express.static()).
  • Third-party middleware: Installed via npm (e.g., cors, morgan, helmet).
  • Error-handling middleware: A special middleware with four arguments used to handle errors.

Application-Level Middleware

Logging Middleware

const express = require('express');
const app = express();

// Logger – runs for every request
app.use(function(req, res, next) {
  const timestamp = new Date().toISOString();
  console.log(`[${timestamp}] ${req.method} ${req.url}`);
  next();
});

app.get('/', function(req, res) {
  res.send('Home Page');
});

app.get('/about', function(req, res) {
  res.send('About Page');
});

app.listen(3000);

Every request — to any route — passes through the logger middleware first.

Applying Middleware to Specific Routes Only

function checkAge(req, res, next) {
  const age = parseInt(req.query.age);
  if (age >= 18) {
    next();
  } else {
    res.status(403).send('Access denied. Must be 18 or older.');
  }
}

// Apply checkAge only to this route
app.get('/adult-content', checkAge, function(req, res) {
  res.send('Welcome! Access granted.');
});

Built-in Middleware

express.json() – Parsing JSON Request Bodies

app.use(express.json());

app.post('/register', function(req, res) {
  const { name, email, password } = req.body;
  console.log("Registering:", name, email);
  res.json({ message: 'User registered successfully', name });
});

Without express.json(), req.body would be undefined for JSON requests.

express.urlencoded() – Parsing HTML Form Data

app.use(express.urlencoded({ extended: true }));

app.post('/login', function(req, res) {
  const { username, password } = req.body;
  res.send('Login attempt by: ' + username);
});

express.static() – Serving Static Files

const path = require('path');
app.use(express.static(path.join(__dirname, 'public')));

Third-Party Middleware

morgan – HTTP Request Logger

npm install morgan
const morgan = require('morgan');

app.use(morgan('dev'));

Morgan automatically logs every request in a clean, colorful format to the terminal. This replaces the manual logger built earlier.

cors – Enable Cross-Origin Requests

npm install cors
const cors = require('cors');

app.use(cors()); // Allow requests from any origin

CORS middleware is essential when building APIs that are accessed from a different domain (e.g., a React frontend on port 5173 calling an Express backend on port 3000).

Router-Level Middleware

// routes/admin.js

const express = require('express');
const router = express.Router();

// Middleware only for admin routes
router.use(function(req, res, next) {
  const isAdmin = req.headers['x-admin-token'] === 'secret123';
  if (isAdmin) {
    next();
  } else {
    res.status(401).json({ error: 'Unauthorized. Admin access only.' });
  }
});

router.get('/dashboard', function(req, res) {
  res.json({ message: 'Admin Dashboard loaded.' });
});

router.delete('/users/:id', function(req, res) {
  res.json({ message: 'User ' + req.params.id + ' deleted by admin.' });
});

module.exports = router;

Error-Handling Middleware

Error-handling middleware in Express has exactly four parameters: err, req, res, next. It must be defined after all other routes and middleware:

const express = require('express');
const app = express();

app.get('/crash', function(req, res, next) {
  const err = new Error("Something crashed!");
  err.statusCode = 500;
  next(err); // Pass the error to the error handler
});

// Error-handling middleware (4 parameters)
app.use(function(err, req, res, next) {
  const statusCode = err.statusCode || 500;
  res.status(statusCode).json({
    error: err.message || 'Internal Server Error'
  });
});

app.listen(3000);

Middleware Execution Order

Middleware in Express executes in the order it is defined. The sequence matters:

app.use(express.json());           // 1. Parse JSON bodies
app.use(morgan('dev'));             // 2. Log requests
app.use(cors());                   // 3. Handle CORS
app.use('/api/users', usersRouter); // 4. Route to handlers
app.use(errorHandler);             // 5. Handle errors (last)

Key Points

  • Middleware functions sit between a request and the route handler, with access to req, res, and next.
  • Always call next() to pass control to the next middleware, unless ending the request.
  • app.use() registers middleware for all routes; it can also target a specific path prefix.
  • Built-in middleware includes express.json(), express.urlencoded(), and express.static().
  • Third-party middleware like morgan and cors are installed via npm and plugged in with app.use().
  • Error-handling middleware must have exactly four parameters and must be defined last.
  • The order in which middleware is defined determines the order it executes.

Leave a Comment

Your email address will not be published. Required fields are marked *