IlmHamroh
JavaScript Full-stack/30-qism. NestJS26/65-dars18 daqiqa
Mundarija (23)

Global enhancer'lar NestJS'da: APP_GUARD, APP_PIPE, APP_FILTER va bajarilish tartibi

Qisqacha: Guard, pipe, interceptor va filtrlar umumiy nom bilan enhancer (kuchaytiruvchi) deyiladi. Ularni global qilishning ikki yo'li bor. app.useGlobalGuards(new G()) — nusxani o'zingiz yaratasiz, DI yo'q: G ning constructor'idagi bog'liqliklar undefined bo'lib qoladi. Modulda { provide: APP_GUARD, useClass: G } — Nest nusxani DI bilan yaratadi, testda AppModule dan ilova yasalganda ham o'z-o'zidan ulanadi. APP_* qaysi modulda yozilmasin — butun ilovaga ta'sir qiladi. Tartib: middleware → guard'lar → interceptor'lar (kirish) → pipe'lar → handler → interceptor'lar (chiqish, teskari) → (xato bo'lsa) filtr; har qatlamda: APP_* → useGlobal* → controller → metod.

Bu darsda

  • APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER va useGlobal* farqini haqiqiy chiqishda ko'rasiz.
  • Xususiyat modulida e'lon qilingan APP_* ham global ekanini bilasiz.
  • So'rov hayotining to'liq tartibini — middleware'dan filtrgacha — bir joyda yig'asiz.
  • Bir nechta global enhancer'ning tartibiga qachon tayanish mumkin, qachon mumkin emasligini bilasiz.
  • vazifalar-api ning AppModule ini yakunlab, loyihaning shu bosqichini commit qilasiz.

Oldin bilishingiz kerak: Request lifecycle, Custom provider'lar, Guard'lar, Interceptor'lar va RxJS, Exception filter'lar.

O'tgan besh darsda AppModule ga birin-ketin APP_PIPE, APP_GUARD, APP_FILTER qo'shildi — har safar "farqi keyinroq" deb. Bu dars — o'sha "keyinroq".

1. Nega bu kerak?

Bootstrap darsida filtrni app.useGlobalFilters(new ProblemDetailsFilter(...)) bilan uladik — sodda va tushunarli. Exception filter'lar darsida esa uni APP_FILTER ga ko'chirdik, chunki filtr logger'ni DI'dan olishi kerak edi. Testda ham farq chiqdi: useGlobalFilters bilan har test faylida filtrni qayta ulash kerak edi, APP_FILTER bilan — yo'q.

Bu ikki farq — DI va testlar — tasodif emas. Global enhancer qanday ulanganiga qarab:

  • uning bog'liqliklari bormi yoki undefined mi;
  • testdagi ilova prod'dagi bilan bir xilmi;
  • bir nechtasi qaysi tartibda ishlaydi.

O'xshatish: binoga yangi qo'riqchi olish. useGlobalGuards(new G()) — ko'chadan odamni chaqirib, eshikka qo'yasiz. U ish joyini biladi, lekin binoning kalitlari, ro'yxatlari, aloqa vositasi unda yo'q. APP_GUARD — kadrlar bo'limi orqali ishga olish: unga kalit, ro'yxat, ratsiya beriladi — tizimning bir qismi.

2. APP_* va useGlobal*

2.1 Ikki yo'l

app.useGlobalGuards(new G()) { provide: APP_GUARD, useClass: G }
Qayerda main.ts / ilova.ts istalgan modulning providers i
Nusxani kim yaratadi siz (new) Nest DI
Constructor bog'liqliklari qo'lda beriladi yoki yo'q DI'dan
Testda (Test.createTestingModule, NestFactory.create(AppModule)) qayta ulash kerak modul bilan birga keladi
Bir nechta bitta chaqiruvda ro'yxat har biri alohida provider

To'rt tur uchun to'rt token: APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER (@nestjs/core dan). Ular — Custom provider'lar darsidagi oddiy tokenlar; Nest ilova yig'ilganda shu tokenli provider'larni topib, global enhancer qilib ulaydi.

2.2 Demo: DI bilan va DI'siz

Bitta guard — SozlamaliGuard — constructor'ida SOZLAMA tokeni orqali sozlama oladi va unga qarab ruxsat beradi. @Optional() — bog'liqlik topilmasa xato emas, undefined (aks holda ilova umuman ishga tushmasdi):

ts
const SOZLAMA = Symbol('SOZLAMA');

@Injectable()
class SozlamaliGuard implements CanActivate {
  constructor(
    @Optional() @Inject(SOZLAMA) private readonly s?: Sozlama,
  ) {}
  canActivate(): boolean {
    console.log(`  [SozlamaliGuard] s=${this.s === undefined
      ? 'undefined' : 'bor'}`);
    return this.s?.ruxsat ?? false;
  }
}

@Module({
  imports: [EksportModule],
  controllers: [VazifalarController],
  providers: [
    { provide: SOZLAMA, useClass: Sozlama },
    { provide: APP_GUARD, useClass: BirinchiGuard },
    { provide: APP_GUARD, useClass: SozlamaliGuard },
  ],
})
class AppModule {}

EksportModule da esa uchinchi guard — o'zining providers ida:

ts
@Module({
  controllers: [EksportController],
  providers: [{ provide: APP_GUARD, useClass: IkkinchiGuard }],
})
class EksportModule {}

Ilovani ikki marta yurgizdik: faqat APP_GUARD lar bilan va ustiga app.useGlobalGuards(new SozlamaliGuard()) qo'shib (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0). So'rov — GET /vazifalar:

text
APP_GUARD:
  [BirinchiGuard]
  [SozlamaliGuard] s=bor
  [IkkinchiGuard]
GET /vazifalar → 200 vazifalar
useGlobalGuards(new …):
  [BirinchiGuard]
  [SozlamaliGuard] s=bor
  [IkkinchiGuard]
  [SozlamaliGuard] s=undefined
GET /vazifalar → 403 {"message":"Forbidden resource","error":"Forbidden","statusCode":403}

Uch narsa:

  • DI: APP_GUARD orqali yaratilgan SozlamaliGuard sozlamani oldi (s=bor). new SozlamaliGuard() bilan yaratilgani — s=undefined, false qaytardi va so'rov 403 oldi. Bir xil klass — ikki xil xulq.
  • Xususiyat modulidagi APP_GUARD ham global: IkkinchiGuard EksportModule da e'lon qilingan, lekin /vazifalar (boshqa modulning controller'i) da ham ishladi. APP_* "shu modul uchun" degani emas — u doim butun ilova uchun.
  • Tartib: APP_GUARD lar useGlobalGuards dan oldin.

@Optional() siz new SozlamaliGuard() holatida ham shu bo'lardi — new DI'ni chetlab o'tadi, @Optional() faqat APP_GUARD yo'lida xatoni yumshatadi.

Diqqat: Xususiyat modulidagi APP_GUARD ni "shu modulning guard'i" deb o'ylash — klassik xato. Yaxshi amaliyot: global enhancer'larni bitta joyda — AppModule da (yoki u import qiladigan maxsus ilova modulida) e'lon qiling, ko'rinib tursin. Modulga xos guard kerak bo'lsa — controller'da @UseGuards(...).

2.3 Model: DI bilan va DI'siz

Nima uchun new bilan bog'liqlik yo'qoladi — kichik DI modelida (DI va IoC konteyner darsidagi g'oya):

js
class Konteyner {
  #provayderlar = new Map();
  qosh(token, qiymat) { this.#provayderlar.set(token, qiymat); }
  yarat(Klass) {
    const deps = (Klass.bogliqliklar ?? []).map(
      (t) => this.#provayderlar.get(t),
    );
    return new Klass(...deps);
  }
}

const SOZLAMA = Symbol("SOZLAMA");
class SozlamaliGuard {
  static bogliqliklar = [SOZLAMA];
  constructor(s) { this.s = s; }
  canActivate() { return this.s?.ruxsat ?? false; }
}

const k = new Konteyner();
k.qosh(SOZLAMA, { ruxsat: true });
console.log("APP_GUARD:", k.yarat(SozlamaliGuard).canActivate());
console.log("new:", new SozlamaliGuard().canActivate());

Konsolda:

text
APP_GUARD: true
new: false

Konteyner klassning "menga nima kerak" ro'yxatini (bogliqliklar) o'qib, ularni topib beradi. new esa ro'yxatga qaramaydi. Nest'da bu ro'yxat design:paramtypes va @Inject(TOKEN) metama'lumotidan olinadi (NestJS nima va nega). Model soddalashtirilgan: haqiqiy Nest modul chegaralari, scope va aylanma bog'liqliklarni ham hisobga oladi.

3. To'liq tartib

3.1 Bitta so'rov — hamma qatlam

Kanon lifecycle tajribasi har qatlamda har darajadagi enhancer'ni yoqib, ular o'z nomini yozgan (kurs tajribasi, NestJS 12.2.0, Vitest ichida). Muvaffaqiyatli GET /t/5. Chiqishni ikki marta o'qing: avval faqat qator boshidagi qatlam nomini (middleware, guard, interceptor, pipe), keyin har qatlam ichidagi daraja tartibini (global → controller → route):

text
1) muvaffaqiyatli GET /t/5 → 200 {"id":"5"}
   1. middleware: global (app.use)
   2. middleware: modul (MiddlewareConsumer)
   3. guard: global (APP_GUARD)
   4. guard: global (useGlobalGuards)
   5. guard: controller
   6. guard: route
   7. interceptor (oldin): global (APP_INTERCEPTOR)
   8. interceptor (oldin): global (useGlobalInterceptors)
   9. interceptor (oldin): controller
  10. interceptor (oldin): route
  11. pipe: global (APP_PIPE) (param:id)
  12. pipe: global (useGlobalPipes) (param:id)
  13. pipe: controller (param:id)
  14. pipe: route (param:id)
  15. pipe: route param (yiqiladi)
  16. HANDLER
  17. interceptor (keyin): route
  18. interceptor (keyin): controller
  19. interceptor (keyin): global (useGlobalInterceptors)
  20. interceptor (keyin): global (APP_INTERCEPTOR)
  21. javob ketdi: 200

(15-qatordagi "yiqiladi" — tajribaning boshqa holatida shu pipe ataylab xato tashlaydi; bu holatda u o'tkazib yubordi.) Butun xarita bitta rasmda:

flowchart TB
  A["middleware<br/>app.use → modul"] --> B["guard'lar<br/>APP → useGlobal → controller → metod"]
  B --> C["interceptor kirish<br/>APP → useGlobal → controller → metod"]
  C --> D["pipe'lar<br/>APP → useGlobal → controller → metod → parametr"]
  D --> E["HANDLER"]
  E --> F["interceptor chiqish<br/>metod → … → APP"]
  F --> G["javob"]
  B -. "xato" .-> X["filtr<br/>metod → controller → global"]
  D -. "xato" .-> X
  E -. "xato" .-> X
  X --> G

Xuddi shu xarita jadval ko'rinishida — har qatlam nimani so'raydi va global ulash tokeni qaysi:

# Qatlam Savoli Global ulash (DI bilan)
1 middleware "So'rovni oldindan tayyorlash kerakmi?" app.use yoki MiddlewareConsumer
2 guard (himoyachi) "Bu so'rov o'tsinmi?" APP_GUARD
3 interceptor (ushlab qoluvchi), kirish "Handler'dan oldin nima qilay?" APP_INTERCEPTOR
4 pipe "Argument to'g'rimi?" APP_PIPE
5 handler asosiy ish —
6 interceptor, chiqish (teskari) "Javobni qanday o'zgartiray?" o'sha APP_INTERCEPTOR
7 exception filter (xato filtri) "Xatoni qanday javobga aylantiray?" APP_FILTER

Middleware'ning APP_* tokeni yo'q: u enhancer emas, Express qatlami (Middleware Nest'da). 7-qatlam faqat xato bo'lganda ishlaydi va 2–6 ning istalganidan "sakrab" kelishi mumkin.

Ikki qoida hamma narsani tushuntiradi:

  1. Qatlamlar tartibi qat'iy: middleware → guard → interceptor → pipe → handler. Shu sabab 415 (guard) validatsiyadan (pipe) oldin chiqadi, ETag (interceptor) javob yuborilishidan oldin qo'yiladi.
  2. Har qatlam ichida — umumiydan xususiyga: APP_* → useGlobal* → controller → metod (pipe'da yana parametr). Chiqishda interceptor'lar — teskari. Filtr esa teskari tanlanadi: eng yaqini (metod) birinchi va yolg'iz (Exception filter'lar).

3.2 Bir nechta APP_* tartibi

Demo'da AppModule ning ikki guard'i yozilgan tartibda ishladi, EksportModule niki — ulardan keyin. Lekin bu tartib modullarni yig'ish tartibiga bog'liq va Nest hujjatida kafolatlangan qoida sifatida yozilmagan (2026-11 holatida). Shuning uchun:

  • Guard'lar orasida bog'liqlik bo'lmasa — tartib muhim emas.
  • Bog'liqlik bo'lsa ("avval kirish, keyin tezlik cheklovi") — ularni bitta modulda, kerakli tartibda e'lon qiling va tartibni test bilan qotiring. Yoki bitta "boshqaruvchi" guard ichida ketma-ket chaqiring.

vazifalar-api auth Nest'ga ko'chganda aynan shunday qilinadi: kirish → tezlik → CAPTCHA → Content-Type tartibi bitta joyda va test bilan (Global auth guard, @Public va @Roles).

Tekshirib ko'ring: ProblemDetailsFilter APP_FILTER bilan ulangan, ustiga main.ts da app.useGlobalFilters(new BoshqaFiltr()) ham bor. Ikkalasi ham @Catch(). Xato tashlanganda qaysi biri ishlaydi?

Javob

Faqat bittasi — filtr xatoni yolg'iz ushlaydi. Global darajadagi ikki filtrdan qaysi biri tanlanishiga tayanmang: bunday konfiguratsiya o'zi xato. Bitta global filtr qoldiring; boshqa xulq kerak bo'lsa — uni o'sha filtr ichida (instanceof bilan) yoki controller/metod darajasidagi @UseFilters bilan qiling.

4. useGlobal* qachon to'g'ri

useGlobal* ni butunlay unutish shart emas:

  • enhancer'ning bog'liqligi yo'q va testlarda unga ehtiyoj yo'q;
  • uni ilova yaratilgandan keyin sozlash kerak (masalan, app.get(HttpAdapterHost) bilan — Exception filter'lar darsidagi BaseExceptionFilter misoli);
  • gibrid ilova (HTTP + mikroservis) — bu yerda ikki yo'l teng: Nest hujjatiga ko'ra (2026-11 holatiga) ulangan mikroservis HTTP qismining global enhancer'larini sukutda olmaydi — useGlobal* bilan ulanganlarini ham, APP_* bilanlarini ham. Meros kerak bo'lsa — connectMicroservice(..., { inheritAppConfig: true }), useGlobal* esa undan oldin chaqiriladi (Mikroservis asoslari NestJS'da).

Qolgan hamma holatda — APP_*.

5. Hujumchi nigohi

  1. Testda himoyasiz ilova. Auth guard main.ts da useGlobalGuards(new AuthGuard(...)) bilan ulangan; testlar AppModule dan ilova yasaydi va guard'siz ishlaydi. "Ruxsatsiz — 401" testi hech qachon yozilmaydi yoki yolg'on o'tadi. Himoya — APP_GUARD: test va prod bir xil.
  2. DI'siz guard'ning sukut javobi. Guard bog'liqligi undefined bo'lganda true qaytarsa (return this.s?.ruxsat ?? true) — hamma o'tadi. Himoya — xavfsizlik guard'larida sukut rad (?? false) va @Optional() dan qochish: bog'liqlik yo'q bo'lsa ilova ishga tushmasin (fail-fast).
  3. Xususiyat modulidagi kutilmagan global guard. Kimdir EksportModule ga "faqat eksport uchun" deb APP_GUARD qo'shadi — u butun API'ni sekinlashtiradi yoki yopadi. Himoya — APP_* faqat AppModule da, review qoidasi.
  4. Tartibga tayangan xavfsizlik. "Avval auth guard, keyin rol guard" — turli modullarda e'lon qilingan bo'lsa, tartib o'zgarishi rol guard'ini req.user siz chaqiradi. Himoya — tartib bitta joyda va testda.

6. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Global guard'da bog'liqlik undefined useGlobalGuards(new G()) APP_GUARD
Testda filtr/guard ishlamaydi useGlobal* faqat main.ts da APP_*
Bir modul guard'i hamma joyda ishlaydi APP_GUARD xususiyat modulida @UseGuards controller'da
Nest can't resolve dependencies of the G (?) APP_GUARD + bog'liqlik modulda yo'q provider'ni modulga qo'shing
Global enhancer'lar tartibi o'zgardi turli modullarda e'lon bitta modulda, test bilan
Ikki global filtr APP_FILTER + useGlobalFilters bitta filtr

7. Mashqlar

Mashqlar kurs/mashqlar/30/26-global-enhancerlar/ da.

1-mashq (oson): to'ldiring

  • DI bilan global guard tokeni —
  • DI bilan global interceptor tokeni —
  • app.useGlobalGuards(new G()) da G ning bog'liqliklari —
  • Guard va pipe'dan qaysi biri oldin —
  • Bir xatoni nechta filtr ushlaydi —
Yechim
  • APP_GUARD — @nestjs/core dan; { provide: APP_GUARD, useClass: G }.
  • APP_INTERCEPTOR — xuddi shunday; APP_PIPE, APP_FILTER ham.
  • undefined — new DI'ni chetlab o'tadi.
  • guard — middleware → guard → interceptor → pipe.
  • 1 — eng yaqin mos filtr, yolg'iz.

2-mashq (o'rta): tartib generatori

Enhancer'lar ro'yxati berilgan: { tur, daraja, nom }; tur — "guard", "interceptor", "pipe"; daraja — "APP", "useGlobal", "controller", "metod". tartib(royxat) funksiyasi so'rov hayotidagi tartibni qaytarsin: avval turlar (guard → interceptor kirish → pipe), har tur ichida darajalar (APP → useGlobal → controller → metod), keyin HANDLER, keyin interceptor'lar teskari ("chiqish"). Ishora: toSorted (sort), indexOf.

Yechim
js
const TURLAR = ["guard", "interceptor", "pipe"];
const DARAJALAR = ["APP", "useGlobal", "controller", "metod"];

function tartib(royxat) {
  const kalit = (e) =>
    TURLAR.indexOf(e.tur) * 10 + DARAJALAR.indexOf(e.daraja);
  const kirish = royxat.toSorted((a, b) => kalit(a) - kalit(b));
  const chiqish = kirish
    .filter((e) => e.tur === "interceptor")
    .toReversed();
  return [
    ...kirish.map((e) => `${e.tur}: ${e.nom}`),
    "HANDLER",
    ...chiqish.map((e) => `interceptor (chiqish): ${e.nom}`),
  ];
}

const royxat = [
  { tur: "pipe", daraja: "APP", nom: "SxemaPipe" },
  { tur: "interceptor", daraja: "metod", nom: "VazifaSarlavhalari" },
  { tur: "guard", daraja: "APP", nom: "JsonTanaGuard" },
  { tur: "interceptor", daraja: "APP", nom: "Log" },
  { tur: "guard", daraja: "metod", nom: "RolGuard" },
];
for (const q of tartib(royxat)) console.log(q);

Konsolda:

text
guard: JsonTanaGuard
guard: RolGuard
interceptor: Log
interceptor: VazifaSarlavhalari
pipe: SxemaPipe
HANDLER
interceptor (chiqish): VazifaSarlavhalari
interceptor (chiqish): Log

Ro'yxat aralash tartibda berilgan edi, natija esa kanon lifecycle tajribasi bilan bir xil shaklda. Bir tur va bir darajadagi ikki enhancer uchun funksiya asl tartibni saqlaydi (toSorted barqaror) — Nest'da ham bir darajadagi ro'yxat yozilgan tartibda.

3-mashq (qiyin): fail-fast konteyner

«Model: DI bilan va DI'siz» bo'limidagi modelga o'xshash, lekin xavfsizroq konteyner yozing: yarat(Klass) bog'liqlik topilmasa undefined bermasin, darhol xato tashlasin: Nest can't resolve dependencies of the SozlamaliGuard (?) uslubida — (?) topilmagan bog'liqlik o'rnida, topilganlari esa token nomi bilan. Symbol nomi — token.description (Symbol).

Yechim
js
class QatiyKonteyner {
  #p = new Map();
  qosh(token, qiymat) { this.#p.set(token, qiymat); }
  yarat(Klass) {
    const tokenlar = Klass.bogliqliklar ?? [];
    const yoq = tokenlar.some((t) => !this.#p.has(t));
    if (yoq) {
      const ro = tokenlar
        .map((t) => (this.#p.has(t) ? t.description : "?"))
        .join(", ");
      const nom = Klass.name;
      throw new Error(
        `Nest can't resolve dependencies of the ${nom} (${ro})`,
      );
    }
    return new Klass(...tokenlar.map((t) => this.#p.get(t)));
  }
}

const LOG = Symbol("LOG");
const SOZLAMA = Symbol("SOZLAMA");
class SozlamaliGuard {
  static bogliqliklar = [LOG, SOZLAMA];
  constructor(log, s) { this.s = s; }
}

const k = new QatiyKonteyner();
k.qosh(LOG, console);
try {
  k.yarat(SozlamaliGuard);
} catch (x) {
  console.log(x.message);
}
k.qosh(SOZLAMA, { ruxsat: true });
console.log(k.yarat(SozlamaliGuard).s);

Konsolda:

text
Nest can't resolve dependencies of the SozlamaliGuard (LOG, ?)
{ ruxsat: true }

Haqiqiy Nest xabari ham shu shaklda boshlanadi (DI va IoC konteyner darsida ko'rgansiz): (?) — qaysi indeksdagi bog'liqlik topilmagani. Bunday konteyner bilan ilova ishga tushishda yiqiladi — prod'da emas. Xavfsizlik guard'lari uchun bu @Optional() + "sukut ruxsat" dan ancha yaxshi.

8. Loyiha qadami: AppModule yakuniy shakli va commit

8.1 src/app.module.ts

O'tgan darslardagi qo'shimchalar yig'ilgan holat. Kanon faylidagi uzun qatorlarni bo'ldik va izohlarni qisqartirdik (kod ma'nosi aynan); nusxani kanon nusxasida tsc --noEmit, ESLint, Vitest va nest build bilan tekshirdik.

ts
// app.module.ts — ildiz modul: ilova shu moduldan boshlab
// yig'iladi (imports daraxti). forRoot(...) — dinamik modul: ombor
// tayyor berilsa (testlar) — shu, aks holda config bo'yicha
// OmborModule.forRootAsync() yaratadi
import { Module } from "@nestjs/common";
import type {
  DynamicModule, MiddlewareConsumer, NestModule,
} from "@nestjs/common";
import { APP_FILTER, APP_GUARD, APP_PIPE } from "@nestjs/core";
import { BogliqliklarModule } from "./bogliqliklar.module.ts";
import type { TashqiBogliqliklar } from "./bogliqliklar.module.ts";
import { OmborModule } from "./ombor.module.ts";
import { VazifalarModule } from "./vazifalar/vazifalar.module.ts";
import { EksportModule } from "./eksport/eksport.module.ts";
import { SxemaPipe } from "./umumiy/sxema.pipe.ts";
import { JsonTanaGuard } from "./umumiy/json-tana.guard.ts";
import { ProblemDetailsFilter } from "./umumiy/problem.filter.ts";
import { eskirgan, ESKI_YOLLAR } from "./umumiy/eskirgan.ts";
import type { VazifaOmbori } from "./vazifalar/ombor.ts";

export type IlovaBogliqliklari = TashqiBogliqliklar & {
  ombor?: VazifaOmbori | undefined;
};

@Module({})
export class AppModule implements NestModule {
  static forRoot(
    { ombor, ...b }: IlovaBogliqliklari,
  ): DynamicModule {
    if (ombor === undefined && b.config === undefined) {
      throw new Error("AppModule.forRoot: ombor yoki config kerak");
    }
    return {
      module: AppModule,
      imports: [
        BogliqliklarModule.forRoot(b),
        ombor === undefined
          ? OmborModule.forRootAsync()
          : OmborModule.forRoot(ombor),
        VazifalarModule,
        EksportModule,
      ],
      // global enhancer'lar DI bilan (tartib — lifecycle):
      // Standard Schema tekshiruvi, 415 guard'i, Problem Details
      // filtri
      providers: [
        { provide: APP_PIPE, useClass: SxemaPipe },
        { provide: APP_GUARD, useClass: JsonTanaGuard },
        { provide: APP_FILTER, useClass: ProblemDetailsFilter },
      ],
    };
  }

  // Funksional middleware: eski versiyasiz yo'llar ishlaydi, lekin
  // Deprecation + Sunset + voris havolasi bilan — hamma yo'lga
  // ("*splat" — Express 5 sintaksisi; eskirgan() o'zi /vazifalar,
  // /teglar prefiksini tekshiradi)
  configure(consumer: MiddlewareConsumer): void {
    consumer.apply(eskirgan(ESKI_YOLLAR)).forRoutes("*splat");
  }
}

Bu faylda endi ilovaning butun "skeleti" bir qarashda ko'rinadi:

  • modullar — imports: bog'liqliklar, ombor, vazifalar, eksport;
  • global enhancer'lar — providers: pipe, guard, filtr, hammasi DI bilan;
  • ilova darajasidagi middleware — configure: eskirgan yo'llar.

ilova.ts da Nest'ga xos global narsa qolmadi — u faqat Express middleware'larini (sorovId, log, helmet, cors, statik, express.json) va versiyalashni sozlaydi. APP_INTERCEPTOR yo'q: vazifalar-api ning yagona interceptor'i (VazifaSarlavhalariInterceptor) global emas — faqat bitta vazifa qaytaradigan marshrutlarda.

8.2 Tekshiruv

Kanon shu holatni to'liq yurgizgan (kanon o'lchovi, IlmHamroh, 2026-10; Postgres 18 bilan, mashina yuklangan):

text
--- npm run tip
exit 0 (15590 ms)
--- npm run lint
exit 0 (24752 ms)
--- npm run test
 ↓ test/konteyner.test.ts (1 test | 1 skipped)
 Test Files  22 passed | 1 skipped (23)
      Tests  117 passed | 1 skipped (118)
exit 0 (12726 ms)
--- npm run build
exit 0 (8171 ms)

konteyner.test (Testcontainers) alohida yurgizilganda — 10/10. Jonli tekshiruv uch saqlash rejimida (Postgres, Prisma, Prisma + kesh) 9 ta so'rov bilan o'tdi. Validatsiya darslaridan oldingi holat bilan farq faqat ikki joyda: sarlavhalar tartibi (masalan, POST javobida Location endi ETag dan oldin — handler sarlavhani interceptor'dan oldin qo'yadi) va 404 dagi Cache-Control (Interceptor'lar va RxJS darsidagi @Header farqi).

Biz ham telefon uchun bo'lingan fayllar bilan qayta qurilgan nusxani Postgres'siz yurgizdik: tip, lint va nest build — toza, Vitest — Tests 75 passed | 6 skipped (81). Keyin ilovani xotira rejimida ishga tushirib, darslarda ko'chgan har qatlamni bittadan so'rov bilan tekshirdik (biz ishga tushirdik: SAQLASH=xotira, Node 24.21.0; faqat kerakli qatorlar):

text
$ curl -X POST -H 'Content-Type: application/json' -d '{"matn":"Non olish #bozor"}' /v1/vazifalar
HTTP/1.1 201 Created
Location: /v1/vazifalar/1
ETag: "1"

$ curl -X POST -H 'Idempotency-Key: k1' … -d '{"matn":"Choy"}' /v1/vazifalar   (ikki marta)
HTTP/1.1 201 Created
HTTP/1.1 201 Created
Idempotency-Replayed: true

$ curl /vazifalar/1
HTTP/1.1 200 OK
Deprecation: @1792800000
Sunset: Sun, 24 Jan 2027 00:00:00 GMT
Link: </v1/vazifalar/1>; rel="successor-version"

$ curl -X POST -H 'Content-Type: text/plain' -d 'salom' /v1/vazifalar
HTTP/1.1 415 Unsupported Media Type

$ curl -X PATCH … -H 'If-Match: W/"1"' -d '{"bajarildi":true}' /v1/vazifalar/1
HTTP/1.1 412 Precondition Failed

(Manzildagi http://127.0.0.1:<port> ni olib tashladik.) Har qator — bir dars:

  • 201 + Location + ETag — @ToplamYoli() va interceptor (Custom dekoratorlar, Interceptor'lar);
  • Idempotency-Replayed — Nest middleware'i, bir so'rovga bir marta (Middleware Nest'da);
  • Deprecation va Link — AppModule.configure dagi eskirgan, req.originalUrl bilan;
  • 415 — JsonTanaGuard, pipe'dan oldin (Guard'lar);
  • 412 — @IfMatchShart(), zaif ETag rad etildi va Problem Details filtri (APP_FILTER) uni javobga aylantirdi.

8.3 Commit

bash
git add -A
M="refactor: idempotentlik va eskirgan — Nest middleware"
M+=" (MiddlewareConsumer), ETag — interceptor,"
M+=" @IfMatchShart/@ToplamYoli/@VazifaYozuvi,"
M+=" Problem filtri APP_FILTER"
git commit -m "$M"

Bu commit bilan vazifalar-api controller'larida @Req() faqat 405 handler'larida va ro'yxatda qoldi, global enhancer'lar — DI bilan, 25-qism middleware'lari — Nest modullarida. Loyihaning keyingi bosqichi — konfiguratsiya va loglar: @nestjs/config, nestjs-pino.

9. Real ishda

  • Katta Nest loyihalarida global enhancer'lar ko'pincha alohida core.module.ts yoki app.module.ts ning providers ida: APP_GUARD (auth, throttler), APP_INTERCEPTOR (log, serializatsiya), APP_FILTER (xato formati), APP_PIPE (validatsiya). Yangi dasturchi uchun — ilovani tushunishning birinchi fayli.
  • Code review: useGlobal* ko'rsangiz — "bog'liqligi bormi? testda ulanganmi?" savollari.
  • Intervyu: "APP_GUARD va useGlobalGuards farqi?", "Nest so'rov hayotidagi tartibni ayting", "xususiyat modulidagi APP_GUARD qayerda ishlaydi?".

Xulosa

  • Enhancer'lar — guard, interceptor, pipe, filtr; global ulash — APP_* (DI bilan) yoki useGlobal* (new, DI'siz).
  • APP_* istalgan modulda e'lon qilinsa ham — butun ilova uchun; testda modul bilan birga keladi.
  • new bilan yaratilgan enhancer bog'liqliklarni olmaydi — xavfsizlik guard'larida bu jim 403 yoki, yomonrog'i, jim ruxsat.
  • Tartib: middleware → guard → interceptor (kirish) → pipe → handler → interceptor (chiqish, teskari); xato — eng yaqin filtr; har qatlamda APP_* → useGlobal* → controller → metod.
  • vazifalar-api: pipe, guard, filtr — AppModule da APP_*; validatsiya va enhancer'lar bosqichi commit qilindi.

Keyingi dars: @nestjs/config — 25-qismdagi config.ts va Zod bilan tekshirilgan .env Nest'ning ConfigModule iga ko'chadi.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Global enhancer'lar NestJS'da: APPGUARD, APPPIPE, APPFILTER va bajarilish tartibi — IlmHamroh