NestJS — Enterprise Node.js

Middleware

15 min Lesson 12 of 30

Middleware

Middleware is a function that runs before the route handler, with access to the request and response objects. If you know Express middleware, this is the same concept — NestJS simply gives it structure and ties it into the module system. It is the first thing to run in the request lifecycle.

What middleware is for

Middleware handles cross-cutting concerns that happen before routing logic: request logging, attaching properties to the request, raw-body parsing, CORS, and similar low-level tasks. It can run code, modify req/res, end the request, or call next() to continue.

Functional middleware

The simplest form is a plain function:

import { Request, Response, NextFunction } from 'express'; export function logger(req: Request, res: Response, next: NextFunction) { console.log(`${req.method} ${req.originalUrl}`); next(); // pass control to the next handler }
Always call next() (or end the response). If middleware neither calls next() nor sends a response, the request hangs forever. This is the most common middleware bug.

Class middleware

For middleware that needs dependencies injected, implement the NestMiddleware interface:

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(`Request to ${req.path}`); next(); } }

Applying middleware

Middleware is not declared in @Module() metadata. Instead, the module implements NestModule and configures it in a configure() method:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; @Module({ controllers: [UsersController] }) export class UsersModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes('users'); // or a controller, or { path, method } } }

You can target specific routes, methods, or controllers, and use .exclude() to skip some paths.

Middleware vs guards vs interceptors

Middleware runs first, before NestJS even knows which route or handler will run. That makes it perfect for generic, route-agnostic work — but it is the wrong tool for authorization or response shaping:

  • Middleware — generic pre-processing (logging, headers, raw body). No access to which handler runs.
  • Guards — authorization decisions, with full execution context.
  • Interceptors — wrap the handler to transform the request/response.
Reach for middleware only when you need it to run before everything else and you do not need NestJS-specific context (like the target handler or its metadata). For authorization, use a guard; for response transformation, use an interceptor.

Summary

Middleware runs first in the request lifecycle, before guards, pipes, and the handler. Write it as a function or a NestMiddleware class, and apply it via a module's configure(consumer) method targeting specific routes. Use it for generic pre-processing — and reach for guards or interceptors when you need NestJS context. Next: guards, which decide whether a request is allowed to proceed.