# Entrevista NestJS: Guards, Interceptors e arquitetura modular > Perguntas frequentes em entrevistas técnicas de NestJS sobre Guards, Interceptors e arquitetura modular, com exemplos concretos em TypeScript e explicações técnicas. - Published: 2026-03-30 - Updated: 2026-05-04 - Author: Anthony Fillion-Maillet - Tags: nestjs, guards, interceptors, architecture, interview - Reading time: 9 min --- As entrevistas técnicas de NestJS focam sistematicamente em três pilares do framework: Guards, Interceptors e arquitetura modular. Esses mecanismos formam a base de qualquer aplicação NestJS bem estruturada, e os recrutadores os utilizam para avaliar o domínio real do framework. > **O que os entrevistadores avaliam** > > Um Guard controla o acesso (quem entra), um Interceptor transforma dados (o que entra e o que sai), e a arquitetura modular organiza tudo. Dominar os três demonstra uma compreensão profunda do NestJS além das operações CRUD básicas. ## Como funcionam os Guards do NestJS e cenários típicos de entrevista Os Guards implementam a interface `CanActivate` e decidem se uma requisição chega ao handler. Diferentemente do middleware, os Guards acessam o contexto de execução do NestJS (`ExecutionContext`), o que permite tomar decisões de autorização com base nos metadados do handler. Pergunta clássica: "Implemente um Guard que verifique os papéis do usuário." ```typescript // roles.guard.ts import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { // Retrieve roles defined via the @Roles() decorator const requiredRoles = this.reflector.getAllAndOverride('roles', [ context.getHandler(), context.getClass(), ]); // No roles required means the route is public if (!requiredRoles) return true; // Extract user from the HTTP request const { user } = context.switchToHttp().getRequest(); // Check if the user holds at least one required role return requiredRoles.some((role) => user.roles?.includes(role)); } } ``` O `Reflector` lê os metadados anexados por decoradores personalizados. O decorador `@Roles()` correspondente tem este formato: ```typescript // roles.decorator.ts import { SetMetadata } from '@nestjs/common'; // Creates a decorator that attaches roles as metadata export const Roles = (...roles: string[]) => SetMetadata('roles', roles); ``` Uma resposta sólida na entrevista vai além do código. A ordem de execução importa: Middleware → Guards → Interceptors (antes) → Pipes → Handler → Interceptors (depois) → Exception Filters. Essa sequência aparece em praticamente toda entrevista de NestJS. ## Interceptors do NestJS: transformar requisições e respostas Os Interceptors implementam `NestInterceptor` e usam RxJS para manipular o fluxo de dados antes e depois do handler. Sua força vem do acesso ao `CallHandler`, que representa o pipeline de execução. Pergunta frequente: "Crie um Interceptor de logging que meça o tempo de execução." ```typescript // logging.interceptor.ts import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class LoggingInterceptor implements NestInterceptor { private readonly logger = new Logger(LoggingInterceptor.name); intercept(context: ExecutionContext, next: CallHandler): Observable { const request = context.switchToHttp().getRequest(); const { method, url } = request; const now = Date.now(); // next.handle() invokes the route handler return next.handle().pipe( tap(() => { // Runs AFTER the handler responds const duration = Date.now() - now; this.logger.log(`${method} ${url} — ${duration}ms`); }), ); } } ``` O operador `tap` do RxJS observa o fluxo sem modificá-lo. Para transformar a resposta, o operador adequado é o `map`: ```typescript // transform.interceptor.ts import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; // Generic interface for a standardized API response interface ApiResponse { data: T; timestamp: string; path: string; } @Injectable() export class TransformInterceptor implements NestInterceptor> { intercept(context: ExecutionContext, next: CallHandler): Observable> { const request = context.switchToHttp().getRequest(); return next.handle().pipe( map((data) => ({ data, timestamp: new Date().toISOString(), path: request.url, })), ); } } ``` Esse padrão de resposta padronizada pontua bem em entrevistas: mostra capacidade de pensar no design da API e na consistência das respostas. > **Guard vs Interceptor vs Middleware** > > Middleware: processamento HTTP bruto (como Express). Guard: decisão booleana de acesso. Interceptor: transformação do fluxo de requisição/resposta com RxJS. Confundir esses três conceitos em uma entrevista é uma red flag imediata. ## Arquitetura modular do NestJS: construir aplicações escaláveis A arquitetura modular separa desenvolvedores júnior de seniores nas entrevistas. O NestJS impõe uma organização baseada em módulos, mas as perguntas reais miram nas decisões arquiteturais: quando criar um módulo e como gerenciar dependências entre módulos. Pergunta avançada: "Como você estruturaria uma aplicação de e-commerce em NestJS com módulos corretamente desacoplados?" ```typescript // orders/orders.module.ts import { Module } from '@nestjs/common'; import { OrdersService } from './orders.service'; import { OrdersController } from './orders.controller'; import { PaymentsModule } from '../payments/payments.module'; import { ProductsModule } from '../products/products.module'; @Module({ // Import modules that OrdersModule depends on imports: [PaymentsModule, ProductsModule], controllers: [OrdersController], providers: [OrdersService], // Expose OrdersService to modules that import OrdersModule exports: [OrdersService], }) export class OrdersModule {} ``` A armadilha clássica de entrevista: dependências circulares. Se `OrdersModule` importa `PaymentsModule` e `PaymentsModule` importa `OrdersModule`, o NestJS falha na inicialização. A saída de emergência usa `forwardRef`: ```typescript // Resolving circular dependencies @Module({ imports: [forwardRef(() => PaymentsModule)], }) export class OrdersModule {} ``` A melhor resposta na entrevista explica que `forwardRef` é um remendo, não uma solução. A correção adequada é refatorar para extrair um módulo compartilhado (`SharedOrderPaymentModule`) ou usar um Event Emitter para desacoplar as comunicações. ## Dynamic Modules e configuração avançada Os Dynamic Modules criam módulos configuráveis e reutilizáveis entre projetos. Esse padrão aparece em entrevistas de nível sênior. Pergunta: "Crie um módulo de cache configurável com opções dinâmicas." ```typescript // cache/cache.module.ts import { Module, DynamicModule, Global } from '@nestjs/common'; import { CacheService } from './cache.service'; // Module configuration interface export interface CacheModuleOptions { ttl: number; // Time-to-live in seconds maxItems: number; // Maximum number of entries prefix: string; // Key prefix } @Global() // Available everywhere without explicit import @Module({}) export class CacheModule { static forRoot(options: CacheModuleOptions): DynamicModule { return { module: CacheModule, providers: [ { // Inject options via a custom token provide: 'CACHE_OPTIONS', useValue: options, }, CacheService, ], exports: [CacheService], }; } } ``` O serviço correspondente injeta as opções via `@Inject()`: ```typescript // cache/cache.service.ts import { Injectable, Inject } from '@nestjs/common'; import { CacheModuleOptions } from './cache.module'; @Injectable() export class CacheService { private store = new Map(); constructor(@Inject('CACHE_OPTIONS') private options: CacheModuleOptions) {} set(key: string, value: any): void { const prefixedKey = `${this.options.prefix}:${key}`; this.store.set(prefixedKey, { value, expiry: Date.now() + this.options.ttl * 1000, }); } get(key: string): T | null { const prefixedKey = `${this.options.prefix}:${key}`; const entry = this.store.get(prefixedKey); // Check expiration before returning if (!entry || entry.expiry < Date.now()) return null; return entry.value as T; } } ``` O padrão `forRoot` / `forRootAsync` é um clássico do NestJS. O `forRootAsync` carrega a configuração a partir de um `ConfigService` injetável, o que constitui a prática recomendada em produção. > **Armadilha de entrevista: @Global()** > > Marcar um módulo como `@Global()` parece conveniente, mas abusar disso cria acoplamentos invisíveis. Mencionar em uma entrevista que `@Global()` deve ser reservado para serviços transversais (config, cache, logger) demonstra maturidade arquitetural. ## Decoradores personalizados e composição de Guards Os decoradores personalizados combinam vários Guards e metadados em uma única anotação. Esse tópico surge em posições de mid-sênior. ```typescript // auth.decorator.ts import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common'; import { JwtAuthGuard } from './jwt-auth.guard'; import { RolesGuard } from './roles.guard'; // Combines JWT authentication + role verification export function Auth(...roles: string[]) { return applyDecorators( SetMetadata('roles', roles), UseGuards(JwtAuthGuard, RolesGuard), ); } ``` Uso em um controller: ```typescript // orders/orders.controller.ts import { Controller, Get, Post, Body } from '@nestjs/common'; import { Auth } from '../auth/auth.decorator'; import { OrdersService } from './orders.service'; @Controller('orders') export class OrdersController { constructor(private ordersService: OrdersService) {} @Get() @Auth('admin', 'manager') // Single decorator replaces UseGuards + Roles findAll() { return this.ordersService.findAll(); } @Post() @Auth('admin') create(@Body() dto: CreateOrderDto) { return this.ordersService.create(dto); } } ``` O padrão `applyDecorators` simplifica o código do controller e centraliza a lógica de autorização. Propor essa abordagem proativamente em uma entrevista é um sinal forte. ## Conclusão - Os Guards usam `CanActivate` e o `Reflector` para decisões de acesso baseadas em metadados do handler - Os Interceptors aproveitam o RxJS (`tap`, `map`) para transformar o fluxo antes e depois do handler - A ordem de execução Middleware → Guards → Interceptors → Pipes → Handler é uma pergunta quase universal - Dependências circulares devem ser resolvidas por refatoração arquitetural, não por `forwardRef` - Dynamic Modules (`forRoot`/`forRootAsync`) demonstram capacidade de construir componentes reutilizáveis - `applyDecorators` compõe vários Guards em um único decorador legível - Pratique essas perguntas com código real em [módulos NestJS](/technologies/node-nestjs/interview-questions/nestjs-modules-di) e [Interceptors](/technologies/node-nestjs/interview-questions/middleware-interceptors) --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture