Tecnologia
Il problema del Request Context in NestJS: DI request-scoped vs AsyncLocalStorage (ALS) — una guida pratica per la produzione (2026)
Una guida approfondita e basata su fonti al problema più doloroso in NestJS: request context (utente, tenant, correlation IDs, tracing) senza trasformare il grafo DI in REQUEST scope. Include confronti, pattern reali e un percorso pratico con @pas7/nestjs-request-context.

Cosa otterrai da questo articolo
Non è un post “teorico”. È una guida pratica applicabile in un vero codebase NestJS — con fonti verificate e un framework decisionale chiaro.
• Che cosa significa davvero “request context” in NestJS (e perché fa male). [1][2][3]
• Perché i provider REQUEST-scoped sembrano facili — ma spesso diventano una trappola di performance e architettura. [1]
• Come AsyncLocalStorage (ALS) risolve il pain core (e dove può ancora fallire). [2][3]
• Come lo fanno le aziende: correlation IDs, tracing, propagazione OpenTelemetry, confini di contesto sicuri. [4][5][6][7]
• Un confronto curato dei pacchetti più rilevanti (con pro/contro e casi d’uso ideali). [8][9][10][11][12]
• Una guida da produzione + pattern di codice copiabili (HTTP + code + microservizi). [2][6][7]
• Dove si colloca @pas7/nestjs-request-context e quando è una scelta competitiva. [10]
Il problema più doloroso in NestJS: request context senza danni collaterali
“Request context” è tutto ciò che vuoi poter leggere in profondità nei servizi senza infilare parametri ovunque: utente corrente, tenant, permessi, request ID, trace IDs, handle transazionale, locale, feature flags e altro.
In NestJS di solito finisci in uno di questi esiti:
- Passi il contesto esplicitamente come parametri (affidabile, ma rumoroso e difficile da mantenere).
- Usi provider REQUEST-scoped (API semplice, ma può rendere una grossa parte dell’app instanziata per request). [1]
Perché la DI REQUEST-scoped diventa una trappola (e perché i team se ne pentono)
NestJS lo dice chiaramente: i provider request-scoped aggiungono overhead perché le istanze vengono create per request e — cosa più importante — lo scope “si propaga” attraverso le dipendenze. [1]
Due citazioni importanti quando disegni l’architettura:
> “Any provider that relies on a request-scoped provider automatically adopts a request scope, and this behavior cannot be altered.” [1]
> “Using request-scoped providers will have an impact on application performance... it will still have to create an instance of your class on each request.” [1]
Ecco come le app reali finiscono nei guai: un singolo “datasource / logger / context” request-scoped diventa dipendenza radice e improvvisamente controller, service e repository vengono ricreati per request. Nest cita anche le app multi-tenant come scenario comune. [1]
Sì, Nest menziona che l’impatto può essere “~5% latency-wise” in un’app ben progettata — ma il costo vero spesso è architetturale: diventa più difficile ragionare su lifetime, caching e sulla “forma” del tuo albero DI. [1]
AsyncLocalStorage in un minuto: perché mappa perfettamente il request context
Node.js AsyncLocalStorage è essenzialmente “thread-local storage per codice async”: ti permette di associare uno stato a una catena di esecuzione (promise/callback) e leggerlo dopo senza passare parametri. [3]
La doc di Node raccomanda AsyncLocalStorage rispetto a implementazioni fai-da-te basate su async_hooks, descrivendolo come performante e memory-safe. [3]
Ma non è magia. La perdita di contesto può ancora accadere in “rare situazioni”, specialmente con API a callback o thenable custom — Node consiglia promisify o AsyncResource per bindare correttamente il contesto di esecuzione. [3]
Quindi la domanda diventa: possiamo avvolgere in modo affidabile il lifecycle della request Nest così che tutto a valle veda lo stesso store? NestJS risponde: sì — via middleware / punto di ingresso del lifecycle. [2]
Direzione ufficiale NestJS: ALS come alternativa al REQUEST scope
NestJS posiziona esplicitamente ALS come modo per propagare stato senza passare parametri e come alternativa ai provider REQUEST-scoped “e alcune delle loro limitazioni”. [2]
Idea core: avvolgere la request presto (middleware) con als.run(store, () => next()). Poi qualsiasi provider può leggere dallo store ALS più tardi.
Esempio minimale in stile NestJS (semplificato dalla ricetta ufficiale): [2]
import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { AsyncLocalStorage } from 'node:async_hooks';
const als = new AsyncLocalStorage<{ requestId: string; userId?: string }>();
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply((req: any, _res: any, next: () => void) => {
const store = {
requestId: String(req.headers['x-request-id'] ?? crypto.randomUUID()),
userId: req.headers['x-user-id'] ? String(req.headers['x-user-id']) : undefined,
};
als.run(store, () => next());
})
.forRoutes('*path');
}
}Questo è il pattern “core pulito”. I setup da produzione aggiungono: parsing sicuro degli header, echo dell’header in response, correlazione con tracing, propagazione code/microservizi e testing.
Come lo fanno davvero le aziende: correlation IDs + tracing + propagazione sicura
Nei sistemi di produzione, il request context non riguarda solo “l’utente corrente”. I driver più comuni sono osservabilità e incident response: servono log, trace e metriche correlabili in modo affidabile tra servizi.
È esattamente ciò di cui tratta la propagazione del contesto OpenTelemetry: spostare trace/span context attraverso confini di processo e rete (di default usando header W3C Trace Context come traceparent). [6][7]
Due takeaway pratici dal mondo enterprise:
- Puoi tenere il contesto “in-process” in ALS, ma oltre i confini devi propagare esplicitamente (header HTTP, metadata messaggi, ecc.). [6][7]
- Non mettere dati sensibili nella propagazione cross-service. OpenTelemetry avverte esplicitamente di non inserire segreti/PII in baggage o key-values propagati. [6]
Nota operativa 2026 da non ignorare se dipendi pesantemente da ALS (direttamente o via APM/tracing): Node.js ha rilasciato una mitigazione per un edge case DoS legato ad async_hooks/ALS a gennaio 2026 e raccomanda l’upgrade a versioni patchate. Team APM e OTel hanno pubblicato guidance e contesto. [4][5]
Panorama pacchetti (2026): cosa esiste, cosa è solido, cosa è rischioso
Sotto trovi un confronto ad alto segnale dei pacchetti usati da team reali per risolvere il request context in NestJS. L’obiettivo non è “chi ha più stelle”, ma: correttezza, ergonomia, integrazioni e quanto evita REQUEST scope.
Tabella comparativa rapida
| Package | Core approach | Best for | Key trade-offs |
|---|---|---|---|
nestjs-cls | ALS + ClsService + Proxy Providers + plugins | context completo + transazioni | più astrazione; API surface da imparare |
@pas7/nestjs-request-context | ALS + typed keys + decorators + adapters | request context strict, leggero e tipizzato | ecosistema più nuovo; dipende dalle integrazioni che ti servono |
@medibloc/nestjs-request-context | request context ALS-based | contesto ALS semplice | scope più stretto rispetto a nestjs-cls |
nestjs-pino (parte context) | integrazione logging via ALS | logging contestuale con Pino | focalizzato sul logging (non un framework generale) |
| DIY (ricetta Nest) | ALS “raw” | minimale + pieno controllo | ti prendi edge case + testing |
Ora i dettagli con fonti:
- nestjs-cls si presenta come modulo continuation-local storage per NestJS basato su AsyncLocalStorage, con use case come request ID tracking, multi-tenancy e propagazione di transazioni senza passare parametri espliciti. [8]
- nestjs-pino documenta un argomento chiave spesso ripetuto: evitare provider REQUEST-scoped per cali di performance, usando AsyncLocalStorage per logger legati alla request. [11]
- La ricetta ufficiale Nest riconosce ALS come alternativa valida al REQUEST scope — ma dice anche che NestJS non offre un’astrazione built-in, quindi o implementi tu o usi una libreria. [2]
Deep dive: quando `nestjs-cls` è la scelta migliore
Se i tuoi bisogni di contesto sono ampi (multi-tenancy + transazioni + proxy providers + “request context dove REQUEST scope non è supportato”), nestjs-cls è spesso la toolbox più completa. Le sue docs enfatizzano use case ampi e menzionano esplicitamente l’evitare approcci REQUEST-scoped “scomodi”. [8]
Motivo frequente di adozione: propagare transazioni senza passare un oggetto transazione ovunque, più un ecosistema di plugin supportati attorno a questo pattern. [8]
Se sei già dentro quell’ecosistema, cambiare dopo raramente conviene — ma se ti servono solo 2–3 campi (requestId, userId, tenantId) potresti volere qualcosa di più piccolo.
Dove si colloca `@pas7/nestjs-request-context` (e quando è competitivo)
@pas7/nestjs-request-context è allineato direttamente a questo problema: fornire request context via AsyncLocalStorage mantenendo singleton il tuo albero DI, con forte focus sulla sicurezza dei tipi tramite typed keys e un’ergonomia pulita per NestJS. [10]
Perché è particolarmente rilevante per questo articolo:
- ContextKey<T> tipizzato per i valori del contesto (meno “magic keys” stringly-typed). [10]
- Ergonomia NestJS: decorator (es. parameter decorators) per leggere contesto senza plumbing. [10]
- Adapter HTTP per Express e Fastify in ambienti NestJS. [10]
- Posizionamento esplicito contro l’approccio REQUEST-scope per performance/architettura. [10]
Caveat (onestà): il repo nota che usare l’adapter Fastify fuori da NestJS è limitato per incompatibilità Fastify + AsyncLocalStorage, quindi trattalo come NestJS-first. [10]
Setup pratico: una guida production-friendly (pattern copia/incolla)
Questa sezione è la checklist “fallo bene”: dove inizializzare il contesto, cosa salvare, come propagare a code/microservizi e come testarlo per evitare leak di contesto.
Campi di contesto consigliati (default pratico)
- requestId: correlation ID stabile; rimandalo come x-request-id in response.
- userId: identificatore interno utente (evita email/telefono).
- tenantId: se multi-tenant.
- traceId / spanId: se usi tracing (o derivali dal contesto OTel).
- authLevel / role: se serve per decisioni di autorizzazione (preferisci check espliciti nei flussi critici).
Pattern A: DIY ALS minimale (ottimo se vuoi full control)
Usa l’approccio middleware della ricetta Nest e implementa un wrapper piccolo (con test). [2]
Pattern B: Usa nestjs-cls quando ti serve l’ecosistema
Se ti servono plugin di propagazione transazioni e proxy provider pattern, nestjs-cls è una scelta forte. [8]
Pattern C: Usa @pas7/nestjs-request-context per typed keys strict + surface area snella
Se ti serve un request context pulito e tipizzato (requestId/userId/tenantId + poco altro) e valorizzi un layer NestJS-first con adapter, la libreria PAS7 è una buona scelta. [10]
Punto di inizializzazione
Inizializza il contesto il prima possibile (middleware è la scelta più sicura per HTTP in Nest). La ricetta Nest usa proprio il middleware. [2]
DI singleton
I provider REQUEST-scoped possono far propagare lo scope nelle dipendenze e aggiungere overhead di instanziazione per request. Nest lo evidenzia. [1]
Propagazione cross-service
ALS funziona solo in-process. Tra servizi, propaga con header/metadata messaggi. OTel usa di default W3C Trace Context (traceparent). [6][7]
Sicurezza
Non propagare dati sensibili in contesto/baggage. OTel avverte esplicitamente su segreti/PII in campi propagati tipo baggage. [6]

Flusso request-context sicuro: inizializza presto, leggi ovunque, propaga esplicitamente oltre i confini
Section pas7-quickstart screenshotPattern di codice: `@pas7/nestjs-request-context` (typed keys + accesso ergonomico)
Sotto trovi pattern illustrativi basati sull’approccio della libreria (typed ContextKey<T>, ergonomia Nest-first, adapter). Verifica sempre i nomi esatti delle API nel repo prima di fissare l’uso in produzione. [10]
1) Definisci typed keys (un modulo/pacchetto condiviso)
import { ContextKey } from '@pas7/nestjs-request-context';
export const REQUEST_ID = new ContextKey<string>('requestId');
export const USER_ID = new ContextKey<string | undefined>('userId');
export const TENANT_ID = new ContextKey<string | undefined>('tenantId');2) Inizializza nel middleware (entry point HTTP)
import { Injectable, NestMiddleware } from '@nestjs/common';
import type { Request, Response } from 'express';
import { RequestContextService } from '@pas7/nestjs-request-context';
import { REQUEST_ID, USER_ID, TENANT_ID } from './ctx.keys';
@Injectable()
export class RequestContextMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: () => void) {
const requestId = String(req.headers['x-request-id'] ?? crypto.randomUUID());
const userId = req.headers['x-user-id'] ? String(req.headers['x-user-id']) : undefined;
const tenantId = req.headers['x-tenant-id'] ? String(req.headers['x-tenant-id']) : undefined;
res.setHeader('x-request-id', requestId);
RequestContextService.run(() => {
RequestContextService.set(REQUEST_ID, requestId);
RequestContextService.set(USER_ID, userId);
RequestContextService.set(TENANT_ID, tenantId);
next();
});
}
}3) Leggi il contesto nei servizi (niente param threading)
import { Injectable } from '@nestjs/common';
import { RequestContextService } from '@pas7/nestjs-request-context';
import { REQUEST_ID, USER_ID } from './ctx.keys';
@Injectable()
export class BillingService {
charge() {
const requestId = RequestContextService.get(REQUEST_ID);
const userId = RequestContextService.get(USER_ID);
return { requestId, userId };
}
}Il vantaggio core: i tuoi servizi restano singleton e puliti, ma accedono comunque a contesto per-request. [10]
Code & microservizi: il confine dove molte implementazioni si rompono
ALS non attraversa magicamente i confini di processo. Se pubblichi un job in coda o chiami un altro servizio, devi propagare esplicitamente il contesto (come requestId / trace context). [6][7]
Un buon default è propagare:
- x-request-id (il tuo correlation ID)
HTTP outbound example (pseudo-pattern)
const headers = {
'x-request-id': RequestContextService.get(REQUEST_ID),
'traceparent': currentTraceparent,
};Queue job example (pseudo-pattern)
await queue.add('jobName', {
data: payload,
ctx: {
requestId: RequestContextService.get(REQUEST_ID),
tenantId: RequestContextService.get(TENANT_ID),
},
});Nel consumer del job, inizializza un nuovo contesto ALS e ripristina i campi propagati prima di eseguire la business logic.
Se scegli consumer request-scoped per i job, Nest nota che creano una nuova istanza per job — trade-off simili allo scope per request HTTP. [13]
Testing: come evitare leak di contesto silenziosi in produzione
I bug di contesto sono famosi perché sembrano ok in locale ma falliscono sotto concorrenza.
La strategia minima di test dovrebbe includere:
- Test paralleli: due request simultanee non devono vedere il contesto l’una dell’altra.
- Test “async boundary”: task ritardati (setTimeout, handler messaggi) devono mantenere il contesto (o intenzionalmente non mantenerlo, se lo progetti così). [3]
La libreria PAS7 include una storia di testing in-repo (e si posiziona con un approccio testkit); se vai DIY, devi costruire guardrail equivalenti. [10]
Cosa scegliere (una regola semplice)
Se vuoi una decisione rapida senza rileggere tutto:
• Scegli DIY ALS se vuoi dipendenze minime e sei pronto a gestire test e casi limite in autonomia. Parti dalla ricetta Nest. [2]
• Scegli
nestjs-clsse ti serve un ecosistema maturo (transazioni, proxy providers, use case più ampi). [8]• Scegli
@pas7/nestjs-request-contextse vuoi un layer snello, type-safe, NestJS-first con adapter ed ergonomia pulita. [10]• Evita REQUEST-scoped DI come default — usalo solo quando serve davvero instanziazione per-request e puoi controllare il blast radius. Nest avverte su performance e cascading scope. [1]
Se stai costruendo un prodotto NestJS serio
Il request context è una di quelle decisioni “piccole” che determinano se la codebase resta pulita a 30 endpoint o collassa a 300.
Se stai costruendo un prodotto NestJS e vuoi aiuto con architettura backend, osservabilità o automazione — PAS7 Studio può aiutarti a progettare e implementare sistemi di livello produzione.
Leggi di più: https://pas7.com.ua/blog
Fonti e riferimenti incrociati
Tutti i link sotto sono direttamente rilevanti per il tema e sono stati usati per supportare affermazioni e confronti sopra.
• 1. NestJS docs — Injection scopes (REQUEST scope, cascading scope, note performance) Read source ↗
• 2. NestJS docs — Async Local Storage recipe (approccio ALS ufficiale e rationale) Read source ↗
• 3. Node.js docs — AsyncLocalStorage & troubleshooting context loss (comportamento e raccomandazioni ufficiali) Read source ↗
• 4. Node.js blog — advisory mitigazione DoS legata a async_hooks/ALS (Gen 2026) Read source ↗
• 5. OpenTelemetry — dichiarazione JS su mitigazione DoS Node.js (Gen 2026) Read source ↗
• 6. OpenTelemetry docs — concetti di context propagation, note di sicurezza e default W3C Trace Context Read source ↗
• 7. W3C — specifica Trace Context (standard `traceparent`) Read source ↗
• 8. nestjs-cls — documentazione ufficiale (use case e approccio ALS) Read source ↗
• 10. PAS7 Studio — repo @pas7/nestjs-request-context (typed keys, adapter, ergonomia, limiti) Read source ↗
• 11. nestjs-pino — repository (argomento ALS per logging contestuale vs REQUEST scope) Read source ↗
• 12. Medibloc — repo nestjs-request-context (libreria ALS request context) Read source ↗
• 13. NestJS docs — Queues (consumer request-scoped instanziati per job) Read source ↗
• 14. Datadog — guidance mitigazione che cita impatto ALS/APM (Gen 2026) Read source ↗
FAQ
No — ma è rischioso come default. Nest avverte che i provider request-scoped impattano performance e possono propagare lo scope nelle dipendenze. Usalo solo quando serve davvero instanziazione per-request. [1]
NestJS fornisce una ricetta ufficiale e spiega come ALS possa essere un’alternativa ai provider REQUEST-scoped, ma non offre un’astrazione built-in. [2]
No. ALS è solo in-process. Tra servizi devi propagare esplicitamente (header/metadata). OpenTelemetry usa W3C Trace Context (`traceparent`) per il tracing. [6][7]
Node nota che la perdita di contesto può accadere in rare situazioni, specialmente con API a callback o thenable custom; le soluzioni includono promisify o AsyncResource per bindare correttamente il contesto. [3]
Sì — mira esattamente a questo pain: request context tipizzato su ALS con ergonomia NestJS-first e adapter. È un’ottima scelta quando vuoi un layer snello e type-safe senza spingere l’app in REQUEST scope. [10]
Sì, tieni Node aggiornato. A gennaio 2026 Node ha rilasciato una mitigazione legata alla dipendenza dell’ecosistema da async_hooks/ALS, e vendor OTel/APM hanno pubblicato guidance di upgrade. [4][5][14]
Vuoi altri deep dive ingegneristici, basati su fonti?
Pubbliciamo articoli pratici e citabili su web engineering, automazione, sicurezza e sviluppo prodotto — focalizzati su decisioni che contano davvero in produzione.