Explicación detallada de Guards, Interceptors y Filters en NestJS

  • 1232Palabras
  • 6Minutos
  • 11 Jul, 2024

NestJS ofrece muchas herramientas y características poderosas para gestionar y manejar solicitudes. En este artículo, exploraremos en profundidad los Guards, Interceptors y Filters en NestJS, y mostraremos ejemplos de su uso en aplicaciones reales. También explicaremos la diferencia entre estos y los Middleware.

Guards

Concepto de Guards

Los Guards son un mecanismo utilizado para controlar el flujo de solicitudes, permitiendo validaciones y verificaciones antes de que la solicitud llegue al controlador. Generalmente se utilizan para implementar lógica de autenticación y autorización.

Creación de un Guard

Para crear un Guard, es necesario implementar la interfaz CanActivate y definir el método canActivate. A continuación se muestra un ejemplo simple de un Guard de autenticación:

1
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
2
import { Observable } from "rxjs";
3
4
@Injectable()
5
export 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
// Lógica de validación
15
return request.headers.authorization === "my-secret-token"; // Ejemplo simple
16
}
17
}

Aplicación de Guards

Los Guards se pueden aplicar a nivel de controlador o de método, utilizando el decorador @UseGuards:

Nivel de controlador
1
import { Controller, Get, UseGuards } from "@nestjs/common";
2
import { AuthGuard } from "./auth.guard";
3
4
@Controller("cats")
5
@UseGuards(AuthGuard)
6
export class CatsController {
7
@Get()
8
findAll() {
9
return "This action returns all cats";
10
}
11
}
Nivel de método
1
import { Controller, Get, UseGuards } from "@nestjs.common";
2
import { AuthGuard } from "./auth.guard";
3
4
@Controller("cats")
5
export class CatsController {
6
@Get()
7
@UseGuards(AuthGuard)
8
findAll() {
9
return "This action returns all cats";
10
}
11
}
Nivel global
1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { AuthGuard } from "./auth.guard";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
app.useGlobalGuards(new AuthGuard());
8
await app.listen(3000);
9
}
10
bootstrap();

Casos de uso de Guards

Los Guards se utilizan a menudo para proteger rutas que requieren autenticación o autorización. Por ejemplo, en una ruta que requiere que el usuario haya iniciado sesión, se puede usar un Guard para verificar la identidad del usuario.

Interceptors

Concepto de Interceptors

Los Interceptors son un mecanismo que permite ejecutar lógica personalizada antes y después de procesar una solicitud. Se utilizan comúnmente para el registro de logs, la transformación de datos de respuesta y el manejo de excepciones.

Creación de un Interceptor

Para crear un Interceptor, es necesario implementar la interfaz NestInterceptor y definir el método intercept. A continuación se muestra un ejemplo de un Interceptor que formatea los datos de respuesta:

1
import {
2
Injectable,
3
NestInterceptor,
4
ExecutionContext,
5
CallHandler,
6
} from "@nestjs/common";
7
import { Observable } from "rxjs";
8
import { map } from "rxjs/operators";
9
10
@Injectable()
11
export class TransformInterceptor implements NestInterceptor {
12
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
13
return next.handle().pipe(map((data) => ({ data })));
14
}
15
}

Aplicación de Interceptors

Los Interceptors se pueden aplicar a nivel de controlador o de método, utilizando el decorador @UseInterceptors:

Nivel de controlador
1
import { Controller, Get, UseInterceptors } from "@nestjs/common";
2
import { TransformInterceptor } from "./transform.interceptor";
3
4
@Controller("cats")
5
@UseInterceptors(TransformInterceptor)
6
export class CatsController {
7
@Get()
8
findAll() {
9
return [{ name: "Tom" }, { name: "Jerry" }];
10
}
11
}
Nivel de método
1
import { Controller, Get, UseInterceptors } from "@nestjs.common";
2
import { TransformInterceptor } from "./transform.interceptor";
3
4
@Controller("cats")
5
export class CatsController {
6
@Get()
7
@UseInterceptors(TransformInterceptor)
8
findAll() {
9
return [{ name: "Tom" }, { name: "Jerry" }];
10
}
11
}
Nivel global
1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { TransformInterceptor } from "./transform.interceptor";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
app.useGlobalInterceptors(new TransformInterceptor());
8
await app.listen(3000);
9
}
10
bootstrap();

Casos de uso de Interceptors

Los Interceptors se utilizan comúnmente para el registro de logs, la transformación de datos de respuesta y el manejo de excepciones. Por ejemplo, en una ruta que requiere que los datos de respuesta tengan un formato específico, se puede usar un Interceptor para transformar los datos.

Filters

Concepto de Filters

Los Filters son un mecanismo utilizado para capturar y manejar excepciones no gestionadas. Pueden capturar cualquier excepción no gestionada en un controlador y ejecutar lógica de manejo de errores personalizada.

Creación de un Filter

Para crear un Filter, es necesario implementar la interfaz ExceptionFilter y definir el método catch. A continuación se muestra un ejemplo de un Filter de excepciones global:

1
import {
2
ExceptionFilter,
3
Catch,
4
ArgumentsHost,
5
HttpException,
6
} from "@nestjs/common";
7
import { Request, Response } from "express";
8
9
@Catch(HttpException)
10
export 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
}

Aplicación de Filters

Los Filters se pueden aplicar a nivel de controlador o de método, utilizando el decorador @UseFilters, o se pueden aplicar globalmente:

Nivel de controlador
1
import { Controller, Get, UseFilters } from "@nestjs.common";
2
import { HttpExceptionFilter } from "./http-exception.filter";
3
4
@Controller("cats")
5
@UseFilters(HttpExceptionFilter)
6
export class CatsController {
7
@Get()
8
findAll() {
9
throw new HttpException("Forbidden", 403);
10
}
11
}
Nivel de método
1
import { Controller, Get, UseFilters } from "@nestjs.common";
2
import { HttpExceptionFilter } from "./http-exception.filter";
3
4
@Controller("cats")
5
export class CatsController {
6
@Get()
7
@UseFilters(HttpExceptionFilter)
8
findAll() {
9
throw new HttpException("Forbidden", 403);
10
}
11
}
Nivel global
1
import { NestFactory } from "@nestjs/core";
2
import { AppModule } from "./app.module";
3
import { HttpExceptionFilter } from "./http-exception.filter";
4
5
async function bootstrap() {
6
const app = await NestFactory.create(AppModule);
7
app.useGlobalFilters(new HttpExceptionFilter());
8
await app.listen(3000);
9
}
10
bootstrap();

Casos de uso de Filters

Los Filters se utilizan comúnmente para el manejo global de errores. Por ejemplo, se puede crear un Filter de excepciones global para manejar todas las excepciones no gestionadas y devolver respuestas de error coherentes.

Middleware

Concepto de Middleware

El Middleware es una función que se ejecuta antes de que la solicitud llegue al manejador de rutas y antes de que la respuesta se envíe al cliente. El Middleware se puede utilizar para manejar solicitudes, modificar respuestas, terminar el ciclo de solicitud-respuesta o llamar a la siguiente función de Middleware.

Creación de Middleware

Para crear un Middleware, es necesario implementar la interfaz NestMiddleware y definir el método use. A continuación se muestra un ejemplo simple de un Middleware de registro de logs:

1
import { Injectable, NestMiddleware } from "@nestjs.common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class LoggerMiddleware implements NestMiddleware {
6
use(req: Request, res: Response, next: NextFunction) {
7
console.log(`Request...`);
8
next();
9
}
10
}

Aplicación de Middleware

El Middleware se puede aplicar en el módulo utilizando el método forRoutes:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs.common";
2
import { CatsController } from "./cats.controller";
3
import { LoggerMiddleware } from "./logger.middleware";
4
5
@Module({
6
controllers: [CatsController],
7
})
8
export class CatsModule implements NestModule {
9
configure(consumer: MiddlewareConsumer) {
10
consumer.apply(LoggerMiddleware).forRoutes(CatsController);
11
}
12
}

Casos de uso de Middleware

El Middleware se utiliza comúnmente para el registro de logs, la validación de solicitudes y la compresión de respuestas. Por ejemplo, en un módulo que requiere registro de logs para todas las solicitudes, se puede usar Middleware para registrar la información de la solicitud.

Diferencias entre Guards, Interceptors, Filters y Middleware

  • Guards (Guardias): Utilizados para controlar si una solicitud puede continuar, comúnmente usados para autenticación y autorización.
  • Interceptors (Interceptores): Utilizados para ejecutar lógica adicional antes y después de una solicitud, comúnmente usados para registro de logs, transformación de datos de respuesta y manejo de excepciones.
  • Filters (Filtros): Utilizados para capturar y manejar excepciones no gestionadas, comúnmente usados para manejo global de errores.
  • Middleware: Funciones que se ejecutan antes de que la solicitud llegue al manejador de rutas y antes de que la respuesta se envíe al cliente, comúnmente usados para registro de logs, validación de solicitudes y manipulación de respuestas.

Conclusión

Este artículo proporcionó una explicación detallada de Guards, Interceptors, Filters y Middleware en NestJS, junto con ejemplos que demuestran sus casos de uso en aplicaciones reales. Al utilizar estas herramientas de manera efectiva, podemos construir aplicaciones NestJS potentes y flexibles. También discutimos las diferencias entre estas herramientas para ayudar a comprender mejor sus aplicaciones.