Interceptors
Interceptors in NestJS v10 are a highly useful feature that allows you to execute operations before and after a controller method runs. They can be used to modify request or response objects, log information, monitor performance, and more.
Introduction to Interceptors
Interceptors are pieces of code that run before and after a controller method is executed. They can modify request or response objects or perform additional tasks such as logging or performance monitoring.
Creating Interceptors
To create an interceptor, you need to implement the NestInterceptor interface.
// src/interceptors/logging.interceptor.ts
import { Injectable, NestInterceptor, CallHandler, ExecutionContext, Logger } 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> {
const now = Date.now();
const logger = new Logger('LoggingInterceptor');
return next.handle().pipe(
tap(() => logger.log(`After... ${Date.now() - now}ms`)),
);
}
}Using Interceptors
Interceptors can be applied at the method level or globally across the application.
// src/controllers/users.controller.ts
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './interceptors/logging.interceptor';
@Controller('users')
export class UsersController {
@Get()
@UseInterceptors(LoggingInterceptor)
findAll() {
return 'List of users';
}
}Global Interceptors
You can set a global interceptor to avoid declaring interceptors for each controller or method.
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './interceptors/logging.interceptor';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
await app.listen(3000);
}
bootstrap();Local Interceptors
Local interceptors can be applied to specific controllers or methods.
// src/controllers/users.controller.ts
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './interceptors/logging.interceptor';
@Controller('users')
export class UsersController {
@Get()
@UseInterceptors(LoggingInterceptor)
findAll() {
return 'List of users';
}
}Multiple Interceptors
You can apply multiple interceptors to a single controller method.
// src/interceptors/error.interceptor.ts
import { Injectable, NestInterceptor, CallHandler, ExecutionContext, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
catchError(error => {
const logger = new Logger('ErrorInterceptor');
logger.error('An error occurred:', error.stack);
throw { statusCode: 500, message: 'Internal Server Error' };
}),
);
}
}
// src/controllers/users.controller.ts
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor, ErrorInterceptor } from './interceptors';
@Controller('users')
export class UsersController {
@Get()
@UseInterceptors(LoggingInterceptor, ErrorInterceptor)
findAll() {
return 'List of users';
}
}Dependency Injection in Interceptors
Interceptors can leverage dependency injection to access other services as needed.
// src/interceptors/logging.interceptor.ts
import { Injectable, NestInterceptor, CallHandler, ExecutionContext, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
constructor(private readonly logger: Logger) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
return next.handle().pipe(
tap(() => this.logger.log(`After... ${Date.now() - now}ms`)),
);
}
}Error Handling in Interceptors
When an interceptor catches an error, you can customize the error response.
// src/interceptors/error.interceptor.ts
import { Injectable, NestInterceptor, CallHandler, ExecutionContext, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
catchError(error => {
const logger = new Logger('ErrorInterceptor');
logger.error('An error occurred:', error.stack);
throw { statusCode: 500, message: 'Internal Server Error' };
}),
);
}
}Custom Error Responses
You can use custom error responses to handle errors more effectively.
// src/interceptors/error.interceptor.ts
import { Injectable, NestInterceptor, CallHandler, ExecutionContext, Logger } from '@nestjs/common';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
catchError(error => {
const logger = new Logger('ErrorInterceptor');
logger.error('An error occurred:', error.stack);
throw { statusCode: 500, message: 'Internal Server Error', error: error.message };
}),
);
}
}Logging
You can use interceptors to log request and response information.
// src/interceptors/logging.interceptor.ts
import { Injectable, NestInterceptor, CallHandler, ExecutionContext, Logger } 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> {
const now = Date.now();
const logger = new Logger('LoggingInterceptor');
const request = context.switchToHttp().getRequest();
logger.log(`Request... ${request.method} ${request.url}`);
return next.handle().pipe(
tap(() => logger.log(`After... ${Date.now() - now}ms`)),
);
}
}Decorators
Decorators in NestJS v10 are a powerful feature that allows you to define component behavior declaratively. They can be used to mark classes, properties, methods, or parameters and add metadata to these elements.
Introduction to Decorators
Decorators are a special type of declaration that can be attached to class declarations, methods, accessors, properties, or parameters. In NestJS, decorators are primarily used to define injection metadata and route paths.
Creating Decorators
To create a decorator, you need to define a decorator function that takes a target object as a parameter and can modify the object or add metadata to it.
// src/decorators/log-method.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const LogMethod = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
console.log(`Calling method with id: ${request.params.id}`);
return request.params.id;
});Using Decorators
Decorators can be used on controller method parameters.
// src/controllers/users.controller.ts
import { Controller, Get, Param, UseInterceptors } from '@nestjs/common';
import { LogMethod } from './decorators/log-method.decorator';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@LogMethod() id: string) {
return `This action returns the user with id ${id}`;
}
}Injecting Metadata
Decorators can be used to add metadata to classes, methods, and other elements.
// src/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);Using Injected Metadata
You can use the @Roles decorator to restrict access.
// src/controllers/admin.controller.ts
import { Controller, Get, UseGuards, Roles } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';
import { RoleGuard } from './guards/role.guard';
import { ROLES_KEY } from './decorators/roles.decorator';
@Controller('admin')
export class AdminController {
@Get()
@UseGuards(AuthGuard, RoleGuard)
@Roles('admin')
index() {
return 'Admin page';
}
}Custom Decorators
You can create custom decorators to meet specific needs.
// src/decorators/log-method.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const LogMethod = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
console.log(`Calling method with id: ${request.params.id}`);
return request.params.id;
});Using Custom Decorators
You can use custom decorators for logging or other operations.
// src/controllers/users.controller.ts
import { Controller, Get, Param, UseInterceptors } from '@nestjs/common';
import { LogMethod } from './decorators/log-method.decorator';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@LogMethod() id: string) {
return `This action returns the user with id ${id}`;
}
}Route Path Decorators
NestJS provides built-in decorators to define route paths.
// src/controllers/users.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@Param('id') id: string) {
return `This action returns the user with id ${id}`;
}
}Controller Decorators
Controller decorators are used to define the route prefix for a controller.
// src/controllers/users.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@Param('id') id: string) {
return `This action returns the user with id ${id}`;
}
}Injection Decorators
Injection decorators are used to extract data from requests.
// src/controllers/users.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@Param('id') id: string) {
return `This action returns the user with id ${id}`;
}
}Parameter Decorators
Parameter decorators are used to extract data from requests.
// src/controllers/users.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@Param('id') id: string) {
return `This action returns the user with id ${id}`;
}
}Using Parameter Decorators
You can use decorators like @Param, @Body, and @Query to extract data from requests.
// src/controllers/users.controller.ts
import { Controller, Get, Param, Body, Query } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get(':id')
findUser(@Param('id') id: string) {
return `This action returns the user with id ${id}`;
}
@Post()
createUser(@Body() user: any) {
return `This action adds a new user: ${JSON.stringify(user)}`;
}
@Get()
findAllUsers(@Query('status') status: string) {
return `This action returns all users with status: ${status}`;
}
}Metadata Decorators
Metadata decorators are used to add metadata to classes, methods, and other elements.
// src/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);Using Metadata Decorators
You can use metadata decorators to restrict access.
// src/controllers/admin.controller.ts
import { Controller, Get, UseGuards, Roles } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';
import { RoleGuard } from './guards/role.guard';
import { ROLES_KEY } from './decorators/roles.decorator';
@Controller('admin')
export class AdminController {
@Get()
@UseGuards(AuthGuard, RoleGuard)
@Roles('admin')
index() {
return 'Admin page';
}
}Dependency Injection in Decorators
Decorators can leverage dependency injection to access other services as needed.
// src/decorators/log-method.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const LogMethod = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
console.log(`Calling method with id: ${request.params.id}`);
return request.params.id;
});



