NestJS — Guard ou Middleware?
Nesse artigo, vamos aprender sobre a diferença entre guards e middlewares em aplicações NestJS.
Recentemente, precisei implementar um módulo de autorização de usuário em um projeto NestJS, e como também sou um programador PHP e amo utilizar o framework Laravel, a primeira coisa que veio na minha cabeça foi criar uma middleware. Mas eu também tenho conhecimento que o NestJS possui guards e foi onde que surgiu a minha dúvida.
Quais são as diferenças entre Guards e Middlewares?
Guards e Middlewares tem propósitos bem parecidos, podem até serem utilizadas para as mesmas finalidades, porém, devemos ter em mente que elas possuem papéis específicos
Guards
O guard é um componente que determina se uma requisição deve ser processada ou não. Ele é uma camada de segurança que tem como objetivo controlar os acessos a determinados recursos de uma aplicação. Os guards no NestJS implementam a interface CanActivate
e são usados para lógicas de autorização.
Exemplo: Você pode ter um guard onde verifica se um determinado usuário possui permissões para cadastrar novos usuários.
@Injectable()
export class CanRegisterUser implements CanActivate {
canActivate(context: ExecutionContext): boolean | Promise<boolean> {
// Lógica da verificação da permissão do usuário
return true; // ou false
}
}
E depois inserir dentro do seu controller.
import { Controller, Post, UseGuards, Body } from '@nestjs/common';
import { CanRegisterUser } from './can-register-user.guard';
@Controller('user')
export class CreateUserController {
@Post()
@UseGuards(CanRegisterUser)
async createUser(@Body() userDTO) {
// Lógica para cadastrar novo usuário
}
}
Middlewares
Podemos dizer que as middlewares são camadas por interceptar as requisições e respostas de cada recurso que ela foi inserida. Seu principal objetivo é intermediar tanto a chegada quanto a saída, para realizar tarefas como de autorização, autenticação, logging e entre outras.
Um detalhe muito legal é que uma middleware pode ser executada em forma de cascata, criando assim camadas de regras.
Em NestJS, as middlewares utilizam a interface NestMiddleware
e possuem acesso ao objeto Request
, Response
e NextFunction
. Elas podem ser aplicadas em nível global, de rota ou controlador.
Exemplo: Você pode ter uma middleware que valida imprime um log no console da requisição que chegou e qual usuário está tentando acessar a rota.
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private readonly logger = new Logger(LoggerMiddleware.name);
use(req: Request, res: Response, next: NextFunction) {
const method = req.method;
const path = req.path;
const userId = req.user ? req.user.id : 'N/A';
// Utiliza o LoggerService para imprimir logs
this.logger.log(`[${new Date().toISOString()}] ${method} ${path} - User ID: ${userId}`);
// Permite que a requisição prossiga para os próximos middlewares ou controladores
next();
}
}
E depois, só essa middleware globalmente na aplicação. Desta forma, todas as requisições da sua aplicação vão imprimir logs das requisições recebidas.
Caso tivesse outra middleware ela seria chamada após a execução da LoggerMiddleware
.
// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
@Module({
// ... outras configurações do módulo
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// Aplica o middleware a todas as rotas
consumer.apply(LoggerMiddleware).forRoutes('*');
}
}
Resumo
O guard determina se uma requisição deve ser processada ou não, implementa a interface CanActivate
e é usado geralmente para proteger rotas.
O middleware é executado durante um processamento de uma requisição HTTP como intermediador, utiliza a interface NestMiddleware
e é usado geralmente para tarefas como autenticação, logging, manipulação de cabeçalhos, etc
Ambos são conceitos importantes para criar aplicações web robustas, permitindo controle de acesso e manipulação de requisições em diferentes pontos do ciclo de vida da aplicação.