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.

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.
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."
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<string[]>('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:
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."
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<any> {
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:
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<T> {
data: T;
timestamp: string;
path: string;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, ApiResponse<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponse<T>> {
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.
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?"
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:
// 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.
Pronto para mandar bem nas entrevistas de Node.js / NestJS?
Pratique com nossos simuladores interativos, flashcards e testes tecnicos.
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."
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():
import { Injectable, Inject } from '@nestjs/common';
import { CacheModuleOptions } from './cache.module';
@Injectable()
export class CacheService {
private store = new Map<string, { value: any; expiry: number }>();
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<T>(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.
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.
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:
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
CanActivatee oReflectorpara 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 applyDecoratorscompõe vários Guards em um único decorador legível- Pratique essas perguntas com código real em módulos NestJS e Interceptors
Comece a praticar!
Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.
Tags
Compartilhar
Artigos relacionados

NestJS + Prisma: o stack backend moderno para Node.js
Guia completo para construir uma API backend moderna com NestJS e Prisma. Configuração, modelos, serviços, transações e boas práticas explicadas.

Perguntas de entrevista Node.js Backend: Guia completo 2026
As 25 perguntas mais comuns em entrevistas de backend Node.js. Event loop, async/await, streams, clustering e performance explicados com respostas detalhadas.

NestJS: Construindo uma API REST Completa
Tutorial completo para construir uma API REST profissional com NestJS. Controllers, Services, Modules, validacao com class-validator e tratamento centralizado de erros.