How to Create an Interceptor in NestJS: Simple Guide
In NestJS, create an interceptor by implementing the
NestInterceptor interface and overriding the intercept method. Then, apply it globally or to specific routes using decorators like @UseInterceptors().Syntax
An interceptor in NestJS is a class that implements the NestInterceptor interface. It must have an intercept(context, next) method that handles the request and response flow.
- context: Provides details about the current request.
- next: A handler to continue the request pipeline.
Return an observable from intercept to manipulate the response.
typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable() export class ExampleInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { // Code before request handling return next.handle().pipe( map(data => { // Code after request handling return data; }), ); } }
Example
This example shows a simple interceptor that logs the time taken by a request and modifies the response by adding a timestamp field.
typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap, map } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const now = Date.now(); console.log('Before handling request'); return next.handle().pipe( tap(() => console.log(`After... ${Date.now() - now}ms`)), map(data => ({ ...data, timestamp: new Date().toISOString() })), ); } } // Usage in a controller import { Controller, Get, UseInterceptors } from '@nestjs/common'; @Controller('test') @UseInterceptors(LoggingInterceptor) export class TestController { @Get() getData() { return { message: 'Hello from NestJS' }; } }
Output
{"message":"Hello from NestJS","timestamp":"2024-06-01T12:00:00.000Z"}
Console logs:
Before handling request
After... 5ms
Common Pitfalls
- Forgetting to add
@Injectable()decorator to the interceptor class causes NestJS to not recognize it. - Not returning
next.handle()observable breaks the request flow. - Trying to use interceptors without importing or applying them via
@UseInterceptors()or globally. - Modifying the response incorrectly by not returning the transformed data inside
map.
typescript
/* Wrong: Missing @Injectable() and not returning next.handle() */ export class BadInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { console.log('Intercepted'); // Missing return next.handle() breaks flow } } /* Right: Proper usage */ import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class GoodInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { console.log('Intercepted'); return next.handle(); } }
Quick Reference
- Implement
NestInterceptorinterface. - Use
@Injectable()decorator. - Override
intercept(context, next)method. - Return
next.handle()observable, optionally transformed. - Apply interceptor with
@UseInterceptors()or globally.
Key Takeaways
Create interceptors by implementing the NestInterceptor interface and overriding intercept method.
Always decorate interceptors with @Injectable() to register them properly.
Return next.handle() observable to continue request processing.
Use RxJS operators like map or tap inside intercept to modify or log responses.
Apply interceptors with @UseInterceptors() on controllers or routes, or globally in main.ts.