PAS7 Studio
Natrag na sve članke

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.

08. velj 2026.· 11 min čitanja· Tehnologija
NestJS request context: request-scoped DI vs AsyncLocalStorage (ALS), logging i tracing

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]

- Koristiš ALS-kontekst (čisti pozivi, provideri ostaju singleton, ali treba ispravan setup i svijest o rubnim slučajevima). [2][3]

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]

TS
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]

Operativna napomena za 2026 (nemoj ignorirati ako se oslanjaš na ALS kroz APM/tracing): Node.js je u siječnju 2026 izbacio mitigaciju za async_hooks/ALS-related DoS edge case i preporučuje patchane verzije. APM i OTel timovi su objavili guidance. [4][5]

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

PackageCore approachBest forKey trade-offs
nestjs-clsALS + ClsService + Proxy Providers + pluginsfull-featured context + transakcijeviše apstrakcije; treba naučiti API surface
@pas7/nestjs-request-contextALS + typed keys + decorators + adaptersstrict, lightweight, typed request contextnoviji ekosustav; ovisi o integracijama koje trebaš
@medibloc/nestjs-request-contextALS-based request contextjednostavan ALS contextuži scope vs nestjs-cls
nestjs-pino (context dio)logging integracija preko ALSkontekstualni logging s Pinofokus na logging (nije generalni context framework)
DIY (Nest recept)raw ALSminimalno + full controlti 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-quickstart

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)

TS
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)

TS
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)

TS
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)

- traceparent (W3C Trace Context) ako imaš distributed tracing. [6][7]

HTTP outbound example (pseudo-pattern)

TS
const headers = {
  'x-request-id': RequestContextService.get(REQUEST_ID),
  'traceparent': currentTraceparent,
};

Queue job example (pseudo-pattern)

TS
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]

- Queue propagation testovi: publish → consume eksplicitno restorea context. [6][7]

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-cls ako trebaš zreli ekosustav (transakcije, proxy provideri, širi use-caseovi). [8]

  • Odaberi @pas7/nestjs-request-context ako ž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

Je li REQUEST scope u NestJS-u uvijek loš?

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]

Je li AsyncLocalStorage “službeno podržan” u NestJS-u?

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]

Može li ALS context magično prijeći microservice ili queue granice?

Ne. ALS je samo in-process. Preko granica moraš propagirati eksplicitno (headeri/metadata). OpenTelemetry defaulta na W3C Trace Context (`traceparent`). [6][7]

Što uzrokuje gubitak ALS konteksta?

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]

Je li `@pas7/nestjs-request-context` relevantan i konkurentan za ovu temu?

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]

Moram li paziti na Node.js security updateove ako koristim ALS?

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.

0

Objavljujemo praktične, citabilne članke o web engineeringu, automatizaciji, sigurnosti i product developmentu — fokusirane na odluke koje stvarno znače u produkciji.

Povezani članci

growth

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.

blogs

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.

telegram-media-saver

Automatsko označavanje i pretraga spremljenih linkova

Integracija s GDrive/S3/Notion za automatsko označavanje i brzu pretragu putem search API-ja

services

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.