Skip to content

03. Middleware & Pipeline

1. The Vanilla Mechanics

Middleware is code that executes before a request reaches the final route handler. In Express, middleware is a function with the signature (req, res, next).

Concept: The Request-Response Pipeline

  • Chaining: Middleware functions can call next() to pass control to the next function.
  • Side Effects: Middleware can modify req or res objects (e.g., adding a req.user property).

The β€œRaw” Implementation (Example)

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

const logger = (req, res, next) => {
  console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
  next(); // Pass to the next middleware or handler
};

const auth = (req, res, next) => {
  if (req.headers.authorization === 'secret-token') {
    next();
  } else {
    res.status(401).send('Unauthorized');
  }
};

app.use(logger);
app.get('/dashboard', auth, (req, res) => {
  res.send('Dashboard access');
});

Challenges:

  • Implicit Dependencies: Middleware often relies on properties added by previous middleware (e.g., req.body added by body-parser).
  • Lack of Structure: Middleware logic can easily become disorganized as the pipeline grows.
  • Typing: In TypeScript, defining the types for modified req objects can be difficult.

2. The NestJS Abstraction

NestJS provides a class-based way to define middleware and integrates it with its dependency injection system.

Key Advantages:

  • NestMiddleware interface: Provides a clear structure and typing for middleware.
  • Dependency Injection: Inject other services directly into your middleware.
  • Granular Control: Apply middleware to specific routes, controllers, or globally.

The NestJS Implementation:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`${new Date().toISOString()} - ${req.method} ${req.originalUrl}`);
    next();
  }
}

// In the module:
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('users');
  }
}

3. Engineering Labs

  • Lab 3.1: Create an Express app with custom middleware that calculates and logs the response time of each request.
  • Lab 3.2: Re-implement the same in NestJS using a class-based LoggerMiddleware. Inject a DateService to handle timestamp generation.