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.

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.
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."
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));
}
}El Reflector lee los metadatos asociados por decoradores personalizados. El decorador @Roles() correspondiente tiene este aspecto:
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."
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`);
}),
);
}
}El operador tap de RxJS observa el flujo sin modificarlo. Para transformar la respuesta, el operador adecuado es 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,
})),
);
}
}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.
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?"
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:
// 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.
¿Listo para aprobar tus entrevistas de Node.js / NestJS?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
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."
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():
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;
}
}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.
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.
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:
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
CanActivatey elReflectorpara 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 applyDecoratorscompone varios Guards en un único decorador legible- Practica estas preguntas con código real en módulos NestJS e Interceptors
¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Etiquetas
Compartir
Artículos relacionados

NestJS + Prisma: el stack backend moderno para Node.js
Guía completa para construir una API backend moderna con NestJS y Prisma. Configuración, modelos, servicios, transacciones y buenas prácticas explicadas.

Preguntas de entrevista Node.js Backend: Guia completa 2026
Las 25 preguntas mas frecuentes en entrevistas de backend Node.js. Event loop, async/await, streams, clustering y rendimiento explicados con respuestas detalladas.

NestJS: Construir una API REST Completa
Tutorial completo para construir una API REST profesional con NestJS. Controladores, Servicios, Modulos, validacion con class-validator y manejo centralizado de errores.