# Colloquio NestJS: Guards, Interceptors e architettura modulare > Domande frequenti nei colloqui tecnici NestJS su Guards, Interceptors e architettura modulare, con esempi concreti in TypeScript e spiegazioni tecniche. - Published: 2026-03-30 - Updated: 2026-05-04 - Author: Anthony Fillion-Maillet - Tags: nestjs, guards, interceptors, architecture, interview - Reading time: 9 min --- I colloqui tecnici NestJS si concentrano costantemente su tre pilastri del framework: Guards, Interceptors e architettura modulare. Questi meccanismi costituiscono la spina dorsale di ogni applicazione NestJS ben strutturata, e i recruiter li utilizzano per valutare la padronanza reale del framework. > **Cosa valutano gli intervistatori** > > Un Guard controlla l'accesso (chi entra), un Interceptor trasforma i dati (cosa entra ed esce), e l'architettura modulare organizza il tutto. Padroneggiare i tre dimostra una comprensione profonda di NestJS oltre le semplici operazioni CRUD. ## Come funzionano i Guards di NestJS e scenari tipici di colloquio I Guards implementano l'interfaccia `CanActivate` e decidono se una richiesta raggiunge l'handler. A differenza del middleware, i Guards accedono al contesto di esecuzione di NestJS (`ExecutionContext`), permettendo decisioni di autorizzazione basate sui metadati dell'handler. Domanda classica: "Implementare un Guard che verifichi i ruoli utente." ```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)); } } ``` Il `Reflector` legge i metadati associati dai decoratori personalizzati. Il decoratore `@Roles()` corrispondente ha questa forma: ```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 risposta solida in colloquio va oltre il codice. L'ordine di esecuzione conta: Middleware → Guards → Interceptors (prima) → Pipes → Handler → Interceptors (dopo) → Exception Filters. Questa sequenza appare praticamente in ogni colloquio NestJS. ## Interceptors di NestJS: trasformare richieste e risposte Gli Interceptors implementano `NestInterceptor` e usano RxJS per manipolare il flusso dati prima e dopo l'handler. La loro forza deriva dall'accesso a `CallHandler`, che rappresenta la pipeline di esecuzione. Domanda frequente: "Creare un Interceptor di logging che misuri il tempo di esecuzione." ```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`); }), ); } } ``` L'operatore `tap` di RxJS osserva il flusso senza modificarlo. Per trasformare la risposta, l'operatore corretto è `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, })), ); } } ``` Questo pattern di risposta standardizzata fa punteggio nei colloqui: mostra la capacità di pensare al design dell'API e alla coerenza delle risposte. > **Guard vs Interceptor vs Middleware** > > Middleware: elaborazione HTTP grezza (come Express). Guard: decisione booleana di accesso. Interceptor: trasformazione del flusso richiesta/risposta con RxJS. Confondere questi tre concetti in un colloquio è una red flag immediata. ## Architettura modulare di NestJS: costruire applicazioni scalabili L'architettura modulare distingue gli sviluppatori junior dai senior nei colloqui. NestJS impone un'organizzazione basata su moduli, ma le vere domande puntano alle decisioni architetturali: quando creare un modulo e come gestire le dipendenze tra moduli. Domanda avanzata: "Come strutturereste un'applicazione e-commerce in NestJS con moduli correttamente disaccoppiati?" ```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 trappola classica del colloquio: le dipendenze circolari. Se `OrdersModule` importa `PaymentsModule` e `PaymentsModule` importa `OrdersModule`, NestJS fallisce all'avvio. La via d'uscita usa `forwardRef`: ```typescript // Resolving circular dependencies @Module({ imports: [forwardRef(() => PaymentsModule)], }) export class OrdersModule {} ``` La risposta più forte in colloquio spiega che `forwardRef` è una soluzione di ripiego, non una vera correzione. La correzione adeguata consiste nel rifattorizzare per estrarre un modulo condiviso (`SharedOrderPaymentModule`) o usare un Event Emitter per disaccoppiare le comunicazioni. ## Dynamic Modules e configurazione avanzata I Dynamic Modules creano moduli configurabili e riutilizzabili tra progetti. Questo pattern compare nei colloqui di livello senior. Domanda: "Creare un modulo cache configurabile con opzioni dinamiche." ```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], }; } } ``` Il servizio corrispondente inietta le opzioni tramite `@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; } } ``` Il pattern `forRoot` / `forRootAsync` è un classico di NestJS. `forRootAsync` carica la configurazione da un `ConfigService` iniettabile, che costituisce la pratica raccomandata in produzione. > **Trappola del colloquio: @Global()** > > Marcare un modulo come `@Global()` sembra comodo, ma abusarne crea accoppiamenti invisibili. Dichiarare in colloquio che `@Global()` deve essere riservato ai servizi trasversali (config, cache, logger) dimostra maturità architetturale. ## Decoratori personalizzati e composizione di Guards I decoratori personalizzati combinano più Guards e metadati in un'unica annotazione. Questo argomento appare nei ruoli 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), ); } ``` Utilizzo in un 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); } } ``` Il pattern `applyDecorators` semplifica il codice del controller e centralizza la logica di autorizzazione. Proporre proattivamente questo approccio in colloquio è un segnale forte. ## Conclusione - I Guards usano `CanActivate` e il `Reflector` per decisioni di accesso basate sui metadati dell'handler - Gli Interceptors sfruttano RxJS (`tap`, `map`) per trasformare il flusso prima e dopo l'handler - L'ordine di esecuzione Middleware → Guards → Interceptors → Pipes → Handler è una domanda quasi universale - Le dipendenze circolari devono essere risolte tramite refactoring architetturale, non con `forwardRef` - I Dynamic Modules (`forRoot`/`forRootAsync`) dimostrano la capacità di costruire componenti riutilizzabili - `applyDecorators` compone più Guards in un unico decoratore leggibile - Esercitatevi con queste domande su codice reale nei [moduli NestJS](/technologies/node-nestjs/interview-questions/nestjs-modules-di) e negli [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/it/blog/node-nestjs/nestjs-guards-interceptors-modular-architecture