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
reqorresobjects (e.g., adding areq.userproperty).
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.bodyadded bybody-parser). - Lack of Structure: Middleware logic can easily become disorganized as the pipeline grows.
- Typing: In TypeScript, defining the types for modified
reqobjects 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:
NestMiddlewareinterface: 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 aDateServiceto handle timestamp generation.