Skip to main content

Command Palette

Search for a command to run...

Interceptors vs Middleware

Published
β€’3 min read
Interceptors vs Middleware

Interceptors

An Interceptor in NestJS is a tool that sits around a function and watches what goes in and what comes out.

It runs:

  • Before a controller method starts

  • After the controller method finishes

This allows us to add extra behavior without changing the original code.

What an Interceptor can do

  • Do something before a function runs (like logging time)

  • Change the result before it is sent to the client

  • Handle or change errors

  • Add extra features like caching or response formatting

  • Stop the function from running if needed

Interceptors must always be written as a class.

They:

  • Use @Injectable()

  • Implement the NestInterceptor interface

  • Have the intercept() method

There is no function-based interceptor.

Why only class-based?

Because interceptors:

  • Use Dependency Injection

  • Work with ExecutionContext

  • Use RxJS Observables

  • Run before and after controller methods

// logging interceptor

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<any> {

    console.log('Before controller method runs');

    return next.handle().pipe(
      tap(() => {
        console.log('After controller method runs');
      }),
    );
  }
}
// Apply to a controller or route 

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';

@Controller('cats')
@UseInterceptors(LoggingInterceptor)
export class CatsController {
  @Get()
  findAll() {
    return 'All cats';
  }
}

Middleware

Middleware is a function that runs before a route handler (controller method) is executed.

It gets access to:

  • the request (req)

  • the response (res)

  • the next() function

Middleware decides whether the request should:

  • continue to the next step

  • or stop there

What Middleware can do

In simple terms, middleware can:

  • Run any code (like logging)

  • Change request data (headers, body, user info)

  • Change response data

  • End the request early (send response)

  • Pass control to the next middleware using next()

If next() is not called, the request stops and never reaches the controller.

You can write middleware in two ways:

  • As a function

  • As a class using @Injectable() and NestMiddleware

// function based
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
};
// class based
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...');
    next();
  }
}
// Apply Middleware in Module

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}

Common Uses of Interceptors

  • Response transformation (formatting output)

  • Caching responses

  • Measuring execution time

  • Error handling or modification

  • Adding extra behavior without changing controller code

  • Logging response data

πŸ‘‰ Best for method-level & response-level tasks

Common Uses of Middleware

  • Logging incoming requests

  • Authentication token extraction (JWT, headers)

  • Request validation (basic)

  • Adding headers (CORS, custom headers)

  • Blocking requests early (unauthorized, rate limit)

  • Parsing data (cookies, body, user info)

πŸ‘‰ Best for request-level tasks

Middleware vs Interceptor Quick View

Use CaseMiddlewareInterceptor
Log incoming requestβœ…βœ…
Modify requestβœ…βŒ
Modify responseβŒβœ…
Authenticationβœ…βŒ
CachingβŒβœ…
Measure execution timeβŒβœ…
Format API responseβŒβœ…

Easy Memory Trick

  • Middleware β†’ Request comes in

  • Interceptor β†’ Method runs & response goes out