# Entrevista NestJS: Guards, Interceptors y arquitectura modular > Preguntas frecuentes en entrevistas técnicas de NestJS sobre Guards, Interceptors y arquitectura modular, con ejemplos concretos en TypeScript y explicaciones técnicas. - Published: 2026-03-30 - Updated: 2026-05-04 - Author: Anthony Fillion-Maillet - Tags: nestjs, guards, interceptors, architecture, interview - Reading time: 9 min --- Las entrevistas técnicas de NestJS se centran sistemáticamente en tres pilares del framework: Guards, Interceptors y arquitectura modular. Estos mecanismos forman la base de toda aplicación NestJS bien estructurada, y los reclutadores los utilizan para evaluar el dominio real del framework. > **Lo que evalúan los entrevistadores** > > Un Guard controla el acceso (quién entra), un Interceptor transforma datos (qué entra y qué sale), y la arquitectura modular organiza todo. Dominar los tres demuestra una comprensión profunda de NestJS más allá de operaciones CRUD básicas. ## Cómo funcionan los Guards de NestJS y escenarios típicos de entrevista Los Guards implementan la interfaz `CanActivate` y deciden si una petición llega al handler. A diferencia de los middleware, los Guards acceden al contexto de ejecución de NestJS (`ExecutionContext`), lo que permite tomar decisiones de autorización basadas en los metadatos del handler. Pregunta clásica: "Implementa un Guard que verifique los roles de usuario." ```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)); } } ``` El `Reflector` lee los metadatos asociados por decoradores personalizados. El decorador `@Roles()` correspondiente tiene este aspecto: ```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); ``` Una respuesta sólida en entrevista va más allá del código. El orden de ejecución importa: Middleware → Guards → Interceptors (antes) → Pipes → Handler → Interceptors (después) → Exception Filters. Esta secuencia aparece en prácticamente todas las entrevistas de NestJS. ## Interceptors de NestJS: transformar peticiones y respuestas Los Interceptors implementan `NestInterceptor` y utilizan RxJS para manipular el flujo de datos antes y después del handler. Su potencia proviene del acceso a `CallHandler`, que representa el pipeline de ejecución. Pregunta frecuente: "Crea un Interceptor de logging que mida el tiempo de ejecución." ```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`); }), ); } } ``` El operador `tap` de RxJS observa el flujo sin modificarlo. Para transformar la respuesta, el operador adecuado es `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, })), ); } } ``` Este patrón de respuesta estandarizada puntúa bien en entrevistas: muestra capacidad para pensar en el diseño de la API y la consistencia de las respuestas. > **Guard vs Interceptor vs Middleware** > > Middleware: procesamiento HTTP en bruto (como Express). Guard: decisión booleana de acceso. Interceptor: transformación del flujo de petición/respuesta con RxJS. Confundir estos tres conceptos en una entrevista es una bandera roja inmediata. ## Arquitectura modular de NestJS: construir aplicaciones escalables La arquitectura modular separa a los desarrolladores junior de los senior en las entrevistas. NestJS impone una organización basada en módulos, pero las preguntas reales apuntan a las decisiones arquitectónicas: cuándo crear un módulo y cómo gestionar las dependencias entre módulos. Pregunta avanzada: "¿Cómo estructurarías una aplicación de e-commerce en NestJS con módulos correctamente 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 {} ``` La trampa clásica de entrevista: las dependencias circulares. Si `OrdersModule` importa `PaymentsModule` y `PaymentsModule` importa `OrdersModule`, NestJS falla al arrancar. La salida de emergencia usa `forwardRef`: ```typescript // Resolving circular dependencies @Module({ imports: [forwardRef(() => PaymentsModule)], }) export class OrdersModule {} ``` La mejor respuesta en entrevista explica que `forwardRef` es un parche, no una solución. La corrección adecuada consiste en refactorizar para extraer un módulo compartido (`SharedOrderPaymentModule`) o usar un Event Emitter para desacoplar las comunicaciones. ## Dynamic Modules y configuración avanzada Los Dynamic Modules permiten crear módulos configurables y reutilizables entre proyectos. Este patrón aparece en entrevistas de nivel senior. Pregunta: "Crea un módulo de caché configurable con opciones 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], }; } } ``` El servicio correspondiente inyecta las opciones mediante `@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; } } ``` El patrón `forRoot` / `forRootAsync` es un clásico de NestJS. `forRootAsync` carga la configuración desde un `ConfigService` inyectable, lo que constituye la práctica recomendada en producción. > **Trampa de entrevista: @Global()** > > Marcar un módulo como `@Global()` parece cómodo, pero abusar de ello crea acoplamientos invisibles. Mencionar en una entrevista que `@Global()` debe reservarse para servicios transversales (config, caché, logger) demuestra madurez arquitectónica. ## Decoradores personalizados y composición de Guards Los decoradores personalizados combinan varios Guards y metadatos en una sola anotación. Este tema aparece en posiciones de mid-senior. ```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 en un controlador: ```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); } } ``` El patrón `applyDecorators` simplifica el código del controlador y centraliza la lógica de autorización. Proponer este enfoque de forma proactiva en una entrevista es una señal fuerte. ## Conclusión - Los Guards usan `CanActivate` y el `Reflector` para decisiones de acceso basadas en metadatos del handler - Los Interceptors aprovechan RxJS (`tap`, `map`) para transformar el flujo antes y después del handler - El orden de ejecución Middleware → Guards → Interceptors → Pipes → Handler es una pregunta casi universal - Las dependencias circulares deben resolverse mediante refactorización arquitectónica, no con `forwardRef` - Los Dynamic Modules (`forRoot`/`forRootAsync`) demuestran capacidad para construir componentes reutilizables - `applyDecorators` compone varios Guards en un único decorador legible - Practica estas preguntas con código real en [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/es/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture