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.

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]
“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]
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]
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 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.
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]
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]
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.
@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]
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]
Rani lifecycle
Inicijaliziraj context što ranije (middleware je najsigurniji za HTTP u Nestu). Nest recept koristi middleware baš zbog toga. [2]
Izbjegni REQUEST scope
REQUEST-scoped provideri mogu “zaraziti” scope kroz dependencies i dodati opterećenje po requestu. Nest eksplicitno upozorava. [1]
Eksplicitno
ALS radi samo in-process. Preko servisa, propagiraj headerima/metadata. OTel defaulta na W3C Trace Context (traceparent). [6][7]
Bez tajni u contextu
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
Snimka zaslona sekcije pas7-quickstartIspod 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]
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]
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]
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]
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: /blog
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]
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)
• 2. NestJS docs — Async Local Storage recipe (službeni ALS pristup i rationale)
• 3. Node.js docs — AsyncLocalStorage & troubleshooting context loss (službeno ponašanje i preporuke)
• 4. Node.js blog — DoS mitigation advisory vezan uz async_hooks/ALS (Jan 2026)
• 5. OpenTelemetry — JS statement o Node.js DoS mitigaciji (Jan 2026)
• 6. OpenTelemetry docs — Context propagation, security notes, W3C Trace Context default
• 7. W3C — Trace Context specifikacija (`traceparent` format)
• 8. nestjs-cls — službena dokumentacija (use-caseovi i ALS pristup)
• 10. PAS7 Studio — repo @pas7/nestjs-request-context (typed keys, adapteri, ergonomija, limitations)
• 11. nestjs-pino — repository (ALS kontekstualni logging argument vs REQUEST scope)
• 12. Medibloc — nestjs-request-context repo (ALS request context library)
• 13. NestJS docs — Queues (request-scoped consumeri, instanca po jobu)
• 14. Datadog — mitigation guidance (ALS/APM impact reference, Jan 2026)
Objavljujemo praktične, citabilne članke o web engineeringu, automatizaciji, sigurnosti i product developmentu — fokusirane na odluke koje stvarno znače u produkciji.
Povezani članci
AI SEO / GEO u 2026: vaši sljedeći kupci nisu ljudi — nego agenti
Pretraživanje se pomiče s klikova na odgovore. Botovi i AI agenti pretražuju, citiraju, preporučuju i sve češće kupuju. Saznajte što znači AI SEO / GEO, zašto klasični SEO više nije dovoljan i kako PAS7 Studio pomaže brendovima pobijediti u agentičkom webu.
Najmoćniji Apple čip? M5 Pro i M5 Max ruše rekorde
Analiza Apple M5 Pro i M5 Max čipova u ožujku 2026. Objašnjavamo zašto se ovi čipovi mogu smatrati najjačim profesionalnim laptop SoC-ovima koje je Apple dosad napravio, kako izgledaju protiv M4 Pro, M4 Max, M1 Pro, M1 Max i što pokazuju u usporedbi s aktualnim Intel i AMD konkurentima.
Automatsko označavanje i pretraga spremljenih linkova
Integracija s GDrive/S3/Notion za automatsko označavanje i brzu pretragu putem search API-ja
Razvoj botova i usluge automatizacije
Profesionalni razvoj Telegram botova i automatizacija poslovnih procesa: chatbotovi, AI asistenti, CRM integracije, automatizacija radnih tijekova.
Web razvoj za vaše poslovanje
Profesionalni razvoj modernih web aplikacija i web stranica
Usluge web razvoja
Profesionalne usluge web razvoja za poslovanje: od landing stranica do složenih web platformi s responzivnim dizajnom i SEO optimizacijom.
Profesionalni razvoj za vaše poslovanje
Kreiramo moderne web rješenja i botove za poduzeća. Saznajte kako vam možemo pomoći u postizanju ciljeva.