Middleware

Run Code Before the Route Handler

Middleware

Middleware is the Express-style hook in Nest. It runs first, can mutate req/res, and must call next() to pass control along.

4 min read Level 2/5 #nestjs#middleware#pipeline
What you'll learn
  • Write a class- or function-style middleware
  • Apply middleware via configure() in a module
  • Scope it to routes, methods, or wildcards

Middleware sits at the bottom of Nest’s request pipeline — it’s the first thing to see an incoming request and runs before guards, pipes, or interceptors. If you’ve written Express middleware, you already know the shape.

A Class Middleware

Class middleware integrates with DI, so it can inject services.

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) {
    const start = Date.now();
    res.on('finish', () => {
      console.log(`${req.method} ${req.url} ${res.statusCode} +${Date.now() - start}ms`);
    });
    next();
  }
}

Applying It

Middleware is registered inside a module’s configure hook, not via a decorator. That lets you scope it precisely.

import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');         // every route in this module
  }
}

The argument to forRoutes can be a string path, a controller class, or an object with path + method for surgical scoping.

configure(consumer: MiddlewareConsumer) {
  consumer
    .apply(AuthMiddleware)
    .exclude(
      { path: 'users/health', method: RequestMethod.GET },
    )
    .forRoutes(UsersController);
}

Functional Middleware

If your middleware has no dependencies, skip the class altogether — Nest accepts plain functions too.

import { Request, Response, NextFunction } from 'express';

export function requestId(req: Request, _res: Response, next: NextFunction) {
  (req as any).id = crypto.randomUUID();
  next();
}

// Module
configure(consumer: MiddlewareConsumer) {
  consumer.apply(requestId).forRoutes('*');
}

Where Middleware Fits

Middleware is the first layer Nest runs on a request. The order is:

middleware guards interceptors (pre) → pipes → handler →
interceptors (post) → exception filters

That order matters: middleware can’t read decorator metadata yet (no ExecutionContext), and it can’t easily short-circuit a route with a typed exception response — guards and filters are better for that. Use middleware for the things Express historically handled: logging, body parsing tweaks, request IDs, low-level CORS.

Pipes — Transform & Validate →