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 Case | Middleware | Interceptor |
| 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



