Recentemente tenho me aprofundado nas ferramentas que o NestJS proporciona por conta de projetos que trabalho atualmente, hoje gostaria de trazer um pouco sobre os Guards e Interceptors.

No Nest, tanto Guards quanto Interceptors são ferramentas poderosas do ciclo de vida das requisições. Mas cada um tem seu papel, e entender quando usar um ou outro pode te ajudar a escrever código mais limpo, seguro e organizado.

Guards

São classes que implementam a interface CanActivate e são responsáveis por determinar se uma rota pode ou não ser acessada. Eles são executados antes do controllers e até antes dos interceptors também.

O que é possível fazer com Guards?

  • Verificar se o usuário está autenticado (JWT, Keycloak, etc)
  • Autorização por roles (admin, user, departamentos)
  • Verificação de escopos de permissões
  • Feature toggles (acesso a funcionalidades baseado em planos de executação)

Exemplo:

@Injectable()  
export class RolesGuard implements CanActivate {
 constructor(private requiredRole: string) {} // Recebe a role necessária no construtor

 canActivate(context: ExecutionContext): boolean {
  const request = context.switchToHttp().getRequest();
  const user = request.user // Assume que o user já foi validado

  // Verifica se o usuer tem a role necessaria para processeguir
  return user?.roles?.includes(this.requiredRole)
 }
}

Aplicando o Guard no Controller

@UseGuard(RolesGuard)
@Get('protected')
public async getProtectEndpoint() {
 return 'Só é acessado se passar pelo Guard'
};

Ou Globalmente

app.useGlobalGuards(new RolesGuard());

Algumas vantagens

  • Rápidos e diretos, impedem que lógica do controller seja executada desnecessariamente.
  • Centralizam regras de acesso

Tradeoff

  • Não são ideias para transformar dados ou tratar respostas.
  • Devem retornar apenas true ou false (ou throw se for necessário lançar alguma exceção)

Interceptors

No NestJS é um decorator (@UseInterceptors()) que permite você aplicar interceptadores em controladores ou métodos de rota. Esses interceptadores é uma poderosa ferramente que permite interceptar, transformar, monitorar ou até substituir o comportamento padrão de requisições e respostas. Eles são como "middlewares inteligentes" que o NestJS proporciona.

O @UseInterceptors() permite executar lógica antes ou depois da execução de um handler (como um método de rota, por exemplo).

  • Como eles funcionam?
    Quando aplicado a um método ou controller, o interceptor intercepta o ciclo de vida da requisição e te dá a chance de:

  • Modificar os dados da requisição antes de chegar no handler

  • Modificar ou formatar a resposta antes dela ser enviada de volta ao cliente (ou front-end)

  • Medir tempo de execução

  • Adicionar lógica comum como logging, cache, autenticação e etc.

Exemplo bem genérico:

@UseInterceptors(MyInterceptor)
@Get('data') 
public async getData() {
    return { hello: 'World' }
}

Nest exemplo, a classe MyInterceptor vai ser executada antes e/ou depois do método getData().

Um Interceptor básico

@Injectable()
export class LoggingIntercetor implement NestInterceptor {
 intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
  console.log('Antes da execução do handler')

  const now = Date.now()

  return next.handle().pipe(
   tap(() => console.log(`Depois... ${Date.now - now}ms`))
  )
 }
}

Esse Interceptor apenas loga o tempo que o método levou para responder.

Tradeoff

  • Podem ser complexos se usados em excesso, o aninhamento de vários interceptadores pode não ser uma boa escolha e é passível de se adotar uma outra estratégia.
  • Lógica errada pode afetar todos os endpoints sem perceber.

O uso de Guards ou Interceptors é situacional, não existe certo ou errado, e sim o que seu projeto e sua aplicação demanda.

Mas ultimamente estou analisando os cenários dessa forma:

  • Use Guards para decidir se a requisição continua.
  • Use Interceptors para transformar o que acontece durante ou depois da execução.

Espero que tenha ficado claro e fácil de se entender o que cada ferramenta é capaz de fazer. Usado com parcimônia pode ser um grande aliado na hora do seu desenvolvimento!