What is an Interceptor?
1. What an Interceptor is
An Interceptor is a class implementing the
NestInterceptorinterface, that can intercept the flow of execution between the client and the controller.
It runs after Guards and Pipes, but before the response is sent to the client. That makes it ideal for:
- logging;
- caching;
- transforming the response;
- measuring execution time;
- wrapping errors;
- adding metadata.
2. The Interceptor interface
Every interceptor implements this method:
intercept(context: ExecutionContext, next: CallHandler): Observable<any>context, access to data about the request (the controller, handler, arguments);next, an object that runs the controller and returns a data stream (Observable).
A simple interceptor example
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
const request = context.switchToHttp().getRequest();
console.log(`-> ${request.method} ${request.url}`);
return next
.handle()
.pipe(tap(() => console.log(`<- Done in ${Date.now() - now}ms`)));
}
}Here:
- The code before
next.handle()runs before the controller; - The code inside
.pipe()runs after the controller returns its result.
3. Attaching interceptors
They can be used at three levels:
| Level | Example | Applies to |
|---|---|---|
| Method | @UseInterceptors(LoggingInterceptor) | one method |
| Controller | @UseInterceptors(LoggingInterceptor) | the whole controller |
| Globally | app.useGlobalInterceptors(new LoggingInterceptor()) | the whole application |
An attachment example
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';
@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController {
@Get()
findAll() {
return ['Alice', 'Bob', 'Charlie'];
}
}For a GET /users request, the console shows:
-> GET /users
<- Done in 2ms4. Examples of using interceptors
| Goal | Example |
|---|---|
| Logging | Execution time, status codes |
| Response transformation | Stripping extra fields, adding metadata |
| Caching | Returning a cached result if there is one |
| Metrics | Sending data to Prometheus/Sentry |
| Error handling | Catching exceptions, returning a unified response |
| Wrapping data | Adding a status/meta info to the JSON |
| Request modification | Changing data in req before the controller is called |
5. An example: transforming the response
import { Injectable, NestInterceptor, CallHandler, ExecutionContext } from '@nestjs/common';
import { map, Observable } from 'rxjs';
@Injectable()
export class TransformResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((data) => ({
status: 'success',
data,
timestamp: new Date().toISOString(),
})),
);
}
}The result:
{
"status": "success",
"data": { "id": 1, "name": "Alice" },
"timestamp": "2025-10-19T09:00:00Z"
}6. NestJS's pipeline execution order
Middleware → Guards → Pipes → Interceptors (before the controller)
→ Controller → Interceptors (after the controller)
→ Exception Filters → ResponseInterceptors "wrap" the controller, they run before and after it. That makes them similar to middleware, but at the NestJS level (with DI, metadata, and RxJS).
Summary
An Interceptor in NestJS is something that can run code before and after a controller method is called.
It's used for:
- logging and metrics;
- transforming the response;
- caching;
- error handling;
- measuring execution time.
It's part of the "execution context pipeline", running after Guards and Pipes, but before the response is sent to the client.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.