In-Depth Explanation of Guards, Interceptors, and Filters in NestJS
- 1081Words
- 5Minutes
- 11 Jul, 2024
NestJS provides many powerful tools and features to manage and handle requests. In this article, we will delve into guards, interceptors, and filters in NestJS, demonstrating their usage with examples in real-world scenarios. We will also explain the differences between them and middleware through case studies.
Guards
Concept of Guards
Guards are mechanisms used to control the flow of requests, allowing verification and checks before the request reaches the handler. They are typically used to implement authentication and authorization logic.
Creating a Guard
To create a guard, you need to implement the CanActivate
interface and define the canActivate
method. Here is a simple authentication guard example:
1import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";2import { Observable } from "rxjs";3
4@Injectable()5export class AuthGuard implements CanActivate {6 canActivate(7 context: ExecutionContext,8 ): boolean | Promise<boolean> | Observable<boolean> {9 const request = context.switchToHttp().getRequest();10 return this.validateRequest(request);11 }12
13 validateRequest(request: any): boolean {14 // Validation logic15 return request.headers.authorization === "my-secret-token"; // Simple example16 }17}
Applying Guards
Guards can be applied at the controller or method level using the @UseGuards
decorator:
Controller Level
1import { Controller, Get, UseGuards } from "@nestjs/common";2import { AuthGuard } from "./auth.guard";3
4@Controller("cats")5@UseGuards(AuthGuard)6export class CatsController {7 @Get()8 findAll() {9 return "This action returns all cats";10 }11}
Method Level
1import { Controller, Get, UseGuards } from "@nestjs/common";2import { AuthGuard } from "./auth.guard";3
4@Controller("cats")5export class CatsController {6 @Get()7 @UseGuards(AuthGuard)8 findAll() {9 return "This action returns all cats";10 }11}
Global Level
1import { NestFactory } from "@nestjs/core";2import { AppModule } from "./app.module";3import { AuthGuard } from "./auth.guard";4
5async function bootstrap() {6 const app = await NestFactory.create(AppModule);7 app.useGlobalGuards(new AuthGuard());8 await app.listen(3000);9}10bootstrap();
Real-World Use Cases for Guards
Guards are often used to protect routes that require authentication or authorization. For example, you can use a guard to verify a user’s identity on a route that requires login.
Interceptors
Concept of Interceptors
Interceptors are mechanisms that can execute custom logic before and after request handling. They are often used for logging, transforming response data, and handling exceptions.
Creating an Interceptor
To create an interceptor, you need to implement the NestInterceptor
interface and define the intercept
method. Here is an example of a response data formatting interceptor:
1import {2 Injectable,3 NestInterceptor,4 ExecutionContext,5 CallHandler,6} from "@nestjs/common";7import { Observable } from "rxjs";8import { map } from "rxjs/operators";9
10@Injectable()11export class TransformInterceptor implements NestInterceptor {12 intercept(context: ExecutionContext, next: CallHandler): Observable<any> {13 return next.handle().pipe(map((data) => ({ data })));14 }15}
Applying Interceptors
Interceptors can be applied at the controller or method level using the @UseInterceptors
decorator:
Controller Level
1import { Controller, Get, UseInterceptors } from "@nestjs/common";2import { TransformInterceptor } from "./transform.interceptor";3
4@Controller("cats")5@UseInterceptors(TransformInterceptor)6export class CatsController {7 @Get()8 findAll() {9 return [{ name: "Tom" }, { name: "Jerry" }];10 }11}
Method Level
1import { Controller, Get, UseInterceptors } from "@nestjs/common";2import { TransformInterceptor } from "./transform.interceptor";3
4@Controller("cats")5export class CatsController {6 @Get()7 @UseInterceptors(TransformInterceptor)8 findAll() {9 return [{ name: "Tom" }, { name: "Jerry" }];10 }11}
Global Level
1import { NestFactory } from "@nestjs/core";2import { AppModule } from "./app.module";3import { TransformInterceptor } from "./transform.interceptor";4
5async function bootstrap() {6 const app = await NestFactory.create(AppModule);7 app.useGlobalInterceptors(new TransformInterceptor());8 await app.listen(3000);9}10bootstrap();
Real-World Use Cases for Interceptors
Interceptors are commonly used for logging, transforming response data, and handling exceptions. For example, you can use an interceptor to format response data uniformly on a route.
Filters
Concept of Filters
Filters are mechanisms used to capture and handle unhandled exceptions. They can capture any unhandled exception in controllers and execute custom error handling logic.
Creating a Filter
To create a filter, you need to implement the ExceptionFilter
interface and define the catch
method. Here is an example of a global exception filter:
1import {2 ExceptionFilter,3 Catch,4 ArgumentsHost,5 HttpException,6} from "@nestjs/common";7import { Request, Response } from "express";8
9@Catch(HttpException)10export class HttpExceptionFilter implements ExceptionFilter {11 catch(exception: HttpException, host: ArgumentsHost) {12 const ctx = host.switchToHttp();13 const response = ctx.getResponse<Response>();14 const request = ctx.getRequest<Request>();15 const status = exception.getStatus();16
17 response.status(status).json({18 statusCode: status,19 timestamp: new Date().toISOString(),20 path: request.url,21 });22 }23}
Applying Filters
Filters can be applied at the controller or method level using the @UseFilters
decorator or globally:
Controller Level
1import { Controller, Get, UseFilters } from "@nestjs.common";2import { HttpExceptionFilter } from "./http-exception.filter";3
4@Controller("cats")5@UseFilters(HttpExceptionFilter)6export class CatsController {7 @Get()8 findAll() {9 throw new HttpException("Forbidden", 403);10 }11}
Method Level
1import { Controller, Get, UseFilters } from "@nestjs.common";2import { HttpExceptionFilter } from "./http-exception.filter";3
4@Controller("cats")5export class CatsController {6 @Get()7 @UseFilters(HttpExceptionFilter)8 findAll() {9 throw new HttpException("Forbidden", 403);10 }11}
Global Level
1import { NestFactory } from "@nestjs/core";2import { AppModule } from "./app.module";3import { HttpExceptionFilter } from "./http-exception.filter";4
5async function bootstrap() {6 const app = await NestFactory.create(AppModule);7 app.useGlobalFilters(new HttpExceptionFilter());8 await app.listen(3000);9}10bootstrap();
Real-World Use Cases for Filters
Filters are commonly used for global error handling. For example, you can create a global exception filter to handle all unhandled exceptions and return consistent error responses.
Middleware
Concept of Middleware
Middleware are functions executed before the request reaches the route handler and before the response is sent to the client. Middleware can handle requests, modify responses, end the request-response cycle, or call the next middleware function.
Creating Middleware
To create middleware, you need to implement the NestMiddleware
interface and define the use
method. Here is a simple logging middleware example:
1import { Injectable, NestMiddleware } from "@nestjs/common";2import { Request, Response, NextFunction } from "express";3
4@Injectable()5export class LoggerMiddleware implements NestMiddleware {6 use(req: Request, res: Response, next: NextFunction) {7 console.log(`Request...`);8 next();9 }10}
Applying Middleware
Middleware can be applied in a module using the forRoutes
method:
1import { Module, NestModule, MiddlewareConsumer } from "@nestjs.common";2import { CatsController } from "./cats.controller";3import { LoggerMiddleware } from "./logger.middleware";4
5@Module({6 controllers: [CatsController],7})8export class CatsModule implements NestModule {9 configure(consumer: MiddlewareConsumer) {10 consumer.apply(LoggerMiddleware).forRoutes(CatsController);11 }12}
Real-World Use Cases for Middleware
Middleware are commonly used for logging, request validation, and response compression. For example, you can use middleware to log request information in a module that requires logging for all requests.
Differences Between Guards, Interceptors, Filters, and Middleware
- Guards: Used to control whether a request can proceed, commonly for authentication and authorization.
- Interceptors: Used to execute additional logic before and after request handling, commonly for logging, response data transformation, and exception handling.
- Filters: Used to capture and handle unhandled exceptions, commonly for global error handling.
- Middleware: Used to execute additional logic before the request reaches the route handler and before the response is sent to the client, commonly for logging, request validation, and response compression.
Conclusion
This article delved into guards, interceptors, filters, and middleware in NestJS, demonstrating their usage with examples in real-world scenarios. By using these tools appropriately, we can build powerful and flexible NestJS applications. We also discussed the differences between these tools to help you better understand their application scenarios.