Skip to main content

What is an Interceptor?

1. What an Interceptor is

An Interceptor is a class implementing the NestInterceptor interface, 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:

javascript
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

javascript
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:

LevelExampleApplies to
Method@UseInterceptors(LoggingInterceptor)one method
Controller@UseInterceptors(LoggingInterceptor)the whole controller
Globallyapp.useGlobalInterceptors(new LoggingInterceptor())the whole application

An attachment example

javascript
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:

javascript
-> GET /users <- Done in 2ms

4. Examples of using interceptors

GoalExample
LoggingExecution time, status codes
Response transformationStripping extra fields, adding metadata
CachingReturning a cached result if there is one
MetricsSending data to Prometheus/Sentry
Error handlingCatching exceptions, returning a unified response
Wrapping dataAdding a status/meta info to the JSON
Request modificationChanging data in req before the controller is called

5. An example: transforming the response

javascript
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:

javascript
{ "status": "success", "data": { "id": 1, "name": "Alice" }, "timestamp": "2025-10-19T09:00:00Z" }

6. NestJS's pipeline execution order

javascript
MiddlewareGuardsPipesInterceptors (before the controller) ControllerInterceptors (after the controller) Exception FiltersResponse

Interceptors "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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.