Tehnologija
NestJS problem Request Contexta: Request-scoped DI vs AsyncLocalStorage (ALS) — praktični vodič za produkciju (2026)
Dubinska, izvorima potkrijepljena analiza jedne od najbolnijih stvari u NestJS-u: request context (user, tenant, correlation ID, tracing) bez pretvaranja DI grafa u REQUEST scope. Uključuje usporedbe, realne pattern-e i praktični vodič s @pas7/nestjs-request-context.

Što dobivaš iz ovog članka
Ovo nije “teorijski post”. Ovo je praktični vodič koji možeš primijeniti u stvarnom NestJS codebaseu — s provjerenim izvorima i jasnim okvirom za odluku.
• Što “request context” zapravo znači u NestJS-u (i zašto boli). [1][2][3]
• Zašto REQUEST-scoped provideri izgledaju lako — ali često postanu performance i arhitekturna zamka. [1]
• Kako AsyncLocalStorage (ALS) rješava core pain (i gdje još može puknuti). [2][3]
• Kako to rade kompanije: correlation ID-jevi, tracing, OpenTelemetry propagacija, sigurne granice konteksta. [4][5][6][7]
• Kurirana usporedba relevantnih paketa (prednosti/nedostaci i za koje potrebe su najbolji). [8][9][10][11][12]
• Vodič za produkciju + code patterni koje možeš kopirati (HTTP + queue + microservices). [2][6][7]
• Gdje se uklapa @pas7/nestjs-request-context i kada je konkurentan izbor. [10]
Najbolniji NestJS problem: request context bez kolateralne štete
“Request context” je sve što želiš čitati duboko u servisima bez provlačenja kroz svaku metodu: current user, tenant, permissions, request ID, trace ID-jevi, transaction handle, locale, feature flags i još.
U NestJS-u obično završiš u jednom od ova tri scenarija:
- Prosljeđuješ kontekst eksplicitno kroz parametre (pouzdano, ali bučno i teško za održavanje).
- Koristiš REQUEST-scoped providere (jednostavan API, ali može pretvoriti velik dio appa u per-request instanciranje). [1]
Zašto REQUEST-scoped DI postaje zamka (i zašto timovi kasnije žale)
NestJS to jasno kaže: request-scoped provideri dodaju dodatno opterećenje jer se instance kreiraju po requestu i — još važnije — scope se “širi” kroz ovisnosti. [1]
Dvije citate koje trebaš imati na umu kad dizajniraš arhitekturu:
> “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]
Kako realne aplikacije upadnu u problem: jedan request-scoped “datasource / logger / context” postane korijenska ovisnost i odjednom se controlleri, servisi i repozitoriji rekreiraju po requestu. Nest posebno spominje multi-tenant aplikacije kao čest primjer. [1]
Da, Nest kaže da utjecaj može biti “~5% latency-wise” u dobro dizajniranoj aplikaciji — ali pravi trošak je često arhitekturni: teže je rezonirati o lifetimeu, caching-u i “obliku” DI stabla. [1]
AsyncLocalStorage u jednoj minuti: zašto savršeno mapira request context
Node.js AsyncLocalStorage je praktički “thread-local storage za async kod”: veže state uz execution chain (promise/callback) i omogućuje ti da ga kasnije čitaš bez prosljeđivanja parametara. [3]
Node docs preporučuju AsyncLocalStorage umjesto DIY implementacije na async_hooks, opisujući ga kao performant i memory-safe. [3]
Ali nije magija. Gubitak konteksta može se dogoditi u “rijetkim situacijama”, posebno oko callback-based API-ja ili custom thenables — Node preporučuje promisify ili AsyncResource za pravilno bindanje execution contexta. [3]
Pitanje je: možemo li pouzdano “omotati” Nest request lifecycle tako da sve downstream vidi isti store? NestJS kaže: da — kroz middleware / entry point lifecyclea. [2]
NestJS službeni smjer: ALS kao alternativa REQUEST scopeu
NestJS eksplicitno pozicionira ALS kao način propagacije statea bez parametara i kao alternativu REQUEST-scoped providerima “i nekim njihovim ograničenjima”. [2]
Core ideja: omotaj request rano (middleware) s als.run(store, () => next()). Onda bilo koji provider kasnije može čitati iz ALS storea.
Minimalni NestJS-style primjer (pojednostavljen iz službenog recepta): [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');
}
}Ovo je “clean core” pattern. Production setupovi dodaju: sigurno parsiranje headera, echo requestId u response, trace korelaciju, queue propagaciju i testiranje.
Kako to rade kompanije: correlation ID + tracing + sigurna propagacija
U produkciji request context nije samo “current user”. Najčešći driveri su observability i incident response: trebaš logove, traceove i metrike koje se pouzdano koreliraju kroz servise.
To je točno ono što OpenTelemetry context propagation radi: prenosi trace/span context kroz procesne i mrežne granice (default W3C Trace Context headeri poput traceparent). [6][7]
Dva praktična takeawaya iz enterprise svijeta:
- In-process kontekst može biti u ALS-u, ali preko service boundaryja moraš ga propagirati eksplicitno (HTTP headeri, message metadata itd.). [6][7]
- Ne stavljaj osjetljive podatke u cross-service propagaciju. OpenTelemetry eksplicitno upozorava protiv secrets/PII u baggage-like propagiranim key-valueovima. [6]
Paketi (2026): što postoji, što je solidno, što je rizično
Ispod je sažeta usporedba paketa koje timovi stvarno koriste za NestJS request context. Cilj nije “tko ima više zvjezdica”, nego: korektnost, ergonomija, integracije i koliko dobro izbjegava REQUEST scope.
Brza tablica usporedbe
| Package | Core approach | Best for | Key trade-offs |
|---|---|---|---|
nestjs-cls | ALS + ClsService + Proxy Providers + plugins | full-featured context + transakcije | više apstrakcije; treba naučiti API surface |
@pas7/nestjs-request-context | ALS + typed keys + decorators + adapters | strict, lightweight, typed request context | noviji ekosustav; ovisi o integracijama koje trebaš |
@medibloc/nestjs-request-context | ALS-based request context | jednostavan ALS context | uži scope vs nestjs-cls |
nestjs-pino (context dio) | logging integracija preko ALS | kontekstualni logging s Pino | fokus na logging (nije generalni context framework) |
| DIY (Nest recept) | raw ALS | minimalno + full control | ti pokrivaš edge caseove + testove |
Detalji sa izvorima:
- nestjs-cls se pozicionira kao continuation-local storage modul za NestJS baziran na AsyncLocalStorage, navodi use-caseove poput request ID trackinga, multi-tenancyja i propagacije transakcija bez parametara. [8]
- nestjs-pino dokumentira ključan argument: izbjegavanje REQUEST-scoped providera zbog performance dropa, te korištenje ALS-a za request-bound loggere. [11]
- Nest službeni recept priznaje ALS kao alternativu REQUEST scopeu — ali i kaže da NestJS ne isporučuje built-in apstrakciju, pa ili implementiraš ili koristiš library. [2]
Deep dive: kada je `nestjs-cls` najbolji izbor
Ako su ti potrebe široke (multi-tenancy + transakcije + proxy provideri + “request context gdje REQUEST scope nije podržan”), nestjs-cls je često najkompletniji toolbox. Docs naglašavaju široke use-caseove i izbjegavanje nezgrapnih REQUEST-scoped pristupa. [8]
Čest razlog: transakcije koje se propagiraju bez prosljeđivanja transaction objekta kroz sve slojeve, plus ekosustav pluginova. [8]
Ako si već duboko u tom ekosustavu, prelazak kasnije često nema smisla — ali ako ti trebaju samo 2–3 polja (requestId, userId, tenantId), možda želiš nešto manje.
Gdje se uklapa `@pas7/nestjs-request-context` (i kada je konkurentan)
@pas7/nestjs-request-context je direktno usklađen s ovim problemom: request context preko AsyncLocalStorage uz singleton DI, s jakim fokusom na tipnu sigurnost (typed keys) i čistu NestJS ergonomiju. [10]
Zašto je relevantan za ovaj članak:
- Typed ContextKey<T> za vrijednosti konteksta (manje stringly-typed “magic keys”). [10]
- NestJS ergonomija: dekoratori (npr. parameter decorators) da čitaš context bez plumbing-a. [10]
- HTTP adapteri za Express i Fastify u NestJS okruženjima. [10]
- Eksplicitno pozicioniranje protiv REQUEST-scope pristupa zbog performance/architecture razloga. [10]
Caveat (pošteno): repo kaže da je Fastify adapter izvan NestJS-a limitiran zbog Fastify + AsyncLocalStorage incompatibilities, pa ga tretiraj kao NestJS-first. [10]
Praktični setup: production-friendly vodič (gotovi patterni)
Ovo je “do it right” checklist: gdje initati context, što spremati, kako propagirati prema queue/microservices i kako testirati da ne shipaš context leakove.
Preporučena polja konteksta (praktični default)
- requestId: stabilan correlation ID; vrati ga kao x-request-id u response.
- userId: interni user identifikator (izbjegni email/telefon).
- tenantId: ako si multi-tenant.
- traceId / spanId: ako imaš tracing (ili izvedi iz OTel contexta).
- authLevel / role: ako treba za auth odluke (u kritičnim flowovima preferiraj eksplicitne provjere).
Pattern A: Minimalni DIY ALS (dobro ako želiš full control)
Koristi Nest recept middleware pristup i implementiraj mali wrapper (s testovima). [2]
Pattern B: nestjs-cls kad trebaš ekosustav
Ako trebaš transaction propagation pluginove i proxy provider pattern, nestjs-cls je jak izbor. [8]
Pattern C: @pas7/nestjs-request-context kad želiš strict typed keys + lean surface
Ako ti treba čisti, tipizirani request context (requestId/userId/tenantId + još malo), i cijeniš NestJS-first adapter layer, PAS7 library je smislen izbor. [10]
Initialization point
Inicijaliziraj context što ranije (middleware je najsigurniji za HTTP u Nestu). Nest recept koristi middleware baš zbog toga. [2]
Zadrži DI singleton
REQUEST-scoped provideri mogu “zaraziti” scope kroz dependencies i dodati opterećenje po requestu. Nest eksplicitno upozorava. [1]
Cross-service propagation
ALS radi samo in-process. Preko servisa, propagiraj headerima/metadata. OTel defaulta na W3C Trace Context (traceparent). [6][7]
Security
Ne propagiraj osjetljive podatke kroz context/baggage. OTel eksplicitno upozorava protiv secrets/PII u baggage-like poljima. [6]

Siguran tok request-contexta: inicijaliziraj rano, čitaj bilo gdje, propagiraj eksplicitno preko granica
Section pas7-quickstart screenshotCode patterni: `@pas7/nestjs-request-context` (typed keys + ergonomija)
Ispod su ilustrativni patterni bazirani na pristupu libraryja (typed ContextKey<T>, Nest-first ergonomija, adapteri). Provjeri točna API imena u repou prije production lock-ina. [10]
1) Definiraj typed keys (jedan modul/shared package)
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) Init u middlewareu (HTTP entry point)
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) Čitaj context u servisima (bez param threading-a)
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 };
}
}Core win: servisi ostaju singleton i čisti, ali i dalje imaju per-request kontekst. [10]
Queue & microservices: granica gdje mnoge implementacije pucaju
ALS ne prelazi procesne granice. Ako šalješ job u queue ili zoveš drugi servis, moraš eksplicitno propagirati context (requestId/trace). [6][7]
Dobar default za propagaciju:
- x-request-id (tvoj 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),
},
});Kod consumera, inicijaliziraj novi ALS context i restoreaj propagirana polja prije business logike.
Ako koristiš request-scoped consumere za jobove, Nest kaže da se instanca kreira po jobu — slični trade-offi kao HTTP request scope. [13]
Testiranje: kako izbjeći tihi context leak u produkciji
Context bugovi su zloglasni jer lokalno izgledaju ok, a pod concurrencyjem puknu.
Minimum test strategija treba uključiti:
- Paralelni request testovi: dvije simultane requeste ne smiju vidjeti tuđi context.
- “Async boundary” testovi: odgođeni taskovi (setTimeout, message handleri) moraju zadržati context (ili ga namjerno ne zadržati, ako tako dizajniraš). [3]
PAS7 library ima testing story u repou (i testkit approach); ako ideš DIY, trebaš ekvivalentne guardraile. [10]
Što odabrati (jednostavno pravilo)
Ako želiš brzu odluku bez ponovnog čitanja svega:
• Odaberi DIY ALS ako želiš minimalne ovisnosti i spreman si pokriti testove i rubne slučajeve. Kreni od Nest recepta. [2]
• Odaberi
nestjs-clsako trebaš zreli ekosustav (transakcije, proxy provideri, širi use-caseovi). [8]• Odaberi
@pas7/nestjs-request-contextako želiš lean, type-safe, NestJS-first context layer s adapterima i čistom ergonomijom. [10]• Izbjegavaj REQUEST-scoped DI kao default — koristi ga samo kad stvarno treba per-request instanciranje i možeš kontrolirati blast radius. Nest upozorava na performance i cascading scope. [1]
Ako gradiš ozbiljan NestJS proizvod
Request context je jedna od onih “malih” odluka koja odlučuje hoće li codebase ostati čist na 30 endpointa ili se raspasti na 300.
Ako gradiš NestJS proizvod i želiš pomoć oko backend arhitekture, observabilityja ili automatizacije — PAS7 Studio može pomoći dizajnirati i implementirati production-grade sustave.
Pročitaj više: https://pas7.com.ua/blog
Izvori i unakrsne reference
Svi linkovi ispod su direktno relevantni i korišteni za factual claimove i usporedbe iznad.
• 1. NestJS docs — Injection scopes (REQUEST scope, cascading scope, performance notes) Read source ↗
• 2. NestJS docs — Async Local Storage recipe (službeni ALS pristup i rationale) Read source ↗
• 3. Node.js docs — AsyncLocalStorage & troubleshooting context loss (službeno ponašanje i preporuke) Read source ↗
• 4. Node.js blog — DoS mitigation advisory vezan uz async_hooks/ALS (Jan 2026) Read source ↗
• 5. OpenTelemetry — JS statement o Node.js DoS mitigaciji (Jan 2026) Read source ↗
• 6. OpenTelemetry docs — Context propagation, security notes, W3C Trace Context default Read source ↗
• 7. W3C — Trace Context specifikacija (`traceparent` format) Read source ↗
• 8. nestjs-cls — službena dokumentacija (use-caseovi i ALS pristup) Read source ↗
• 10. PAS7 Studio — repo @pas7/nestjs-request-context (typed keys, adapteri, ergonomija, limitations) Read source ↗
• 11. nestjs-pino — repository (ALS kontekstualni logging argument vs REQUEST scope) Read source ↗
• 12. Medibloc — nestjs-request-context repo (ALS request context library) Read source ↗
• 13. NestJS docs — Queues (request-scoped consumeri, instanca po jobu) Read source ↗
• 14. Datadog — mitigation guidance (ALS/APM impact reference, Jan 2026) Read source ↗
FAQ
Ne — ali je rizičan kao default. Nest upozorava da request-scoped provideri utječu na performance i mogu proširiti scope kroz dependencies. Koristi ga samo kad stvarno treba per-request instanciranje. [1]
NestJS ima službeni recept i objašnjava kako ALS može biti alternativa REQUEST-scoped providerima, ali ne isporučuje built-in apstrakciju. [2]
Ne. ALS je samo in-process. Preko granica moraš propagirati eksplicitno (headeri/metadata). OpenTelemetry defaulta na W3C Trace Context (`traceparent`). [6][7]
Node navodi da se gubitak konteksta može dogoditi u rijetkim situacijama, posebno oko callback API-ja ili custom thenables; preporuke su promisify ili AsyncResource. [3]
Da — cilja baš ovaj pain: tipizirani request context preko ALS-a s NestJS-first ergonomijom i adapterima. Dobar je fit kad želiš lean, type-safe layer bez guranja appa u REQUEST scope. [10]
Da. Drži Node patchanim. U siječnju 2026 Node je izdao mitigaciju vezanu uz async_hooks/ALS ekosustav, a OTel/APM vendor-i su dali upgrade guidance. [4][5][14]
Želiš još dubinskih, izvorima potkrijepljenih engineering analiza?
Objavljujemo praktične, citabilne članke o web engineeringu, automatizaciji, sigurnosti i product developmentu — fokusirane na odluke koje stvarno znače u produkciji.