Middleware

⭐ Interview Importance: LOW
⏱️ Revision Time: 11 min

Middleware is a function which is called before the route handler. Middleware functions have access to the request and response objects, and the next() middleware function in the application’s request-response cycle.

Overview

If you’ve used Express.js, you’re already familiar with middleware. NestJS middleware is entirely equivalent to Express middleware.

Middleware executes very early in the request lifecycle—before Guards, Interceptors, Pipes, or Controllers. This makes it ideal for tasks that need to happen immediately upon receiving a request.

Key Concepts

  • Functional Middleware: A simple function (req, res, next) => {}.
  • Class-based Middleware: A class annotated with @Injectable() that implements the NestMiddleware interface. This allows you to use Dependency Injection inside your middleware.
  • Execution Order: Middleware functions are executed sequentially in the order they are registered.
  • Routing: Middleware isn’t applied using decorators like @UseGuards(). Instead, you configure it in the module class using the configure() method of the NestModule interface.

Code Examples

Class-based Middleware

This is preferred when you need to inject services into your middleware.

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

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  private logger = new Logger('HTTP');

  // Must implement the 'use' method
  use(req: Request, res: Response, next: NextFunction) {
    const { ip, method, originalUrl } = req;
    
    // Log before passing control to the next function
    this.logger.log(`Incoming Request: ${method} ${originalUrl} - IP: ${ip}`);
    
    // VERY IMPORTANT: You must call next(), otherwise the request will hang!
    next();
  }
}

Applying Middleware in a Module

To apply middleware, the module must implement the NestModule interface.

import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
import { UsersModule } from './users/users.module';

@Module({
  imports: [UsersModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      // Apply to all routes
      .forRoutes('*'); 
      
      /* You can also restrict it:
      .exclude({ path: 'users', method: RequestMethod.GET })
      .forRoutes(UsersController);
      */
  }
}

Best Practices

  • Use Middleware for Framework-level tasks: Middleware is great for parsing cookies, setting security headers (like Helmet), handling CORS, or basic request logging.
  • Prefer Guards for Auth: While you can do authentication in middleware (like passport.js does natively), Nest’s Guards are integrated deeply into the Execution Context and work seamlessly with features like Swagger and WebSockets.
  • Always Call next(): If your middleware does not explicitly return a response (e.g., res.send()), it must call next(). If you forget, the request will timeout and hang indefinitely.