Mundarija (23)
- Bu darsda
- 1. Nega bu kerak?
- 2. APP_* va useGlobal*
- 2.1 Ikki yo'l
- 2.2 Demo: DI bilan va DI'siz
- 2.3 Model: DI bilan va DI'siz
- 3. To'liq tartib
- 3.1 Bitta so'rov — hamma qatlam
- 3.2 Bir nechta APP_* tartibi
- 4. useGlobal* qachon to'g'ri
- 5. Hujumchi nigohi
- 6. Ko'p uchraydigan xatolar
- 7. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): tartib generatori
- 3-mashq (qiyin): fail-fast konteyner
- 8. Loyiha qadami: AppModule yakuniy shakli va commit
- 8.1 src/app.module.ts
- 8.2 Tekshiruv
- 8.3 Commit
- 9. Real ishda
- Xulosa
- Manbalar
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:Gning constructor'idagi bog'liqliklarundefinedbo'lib qoladi. Modulda{ provide: APP_GUARD, useClass: G }— Nest nusxani DI bilan yaratadi, testdaAppModuledan 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_FILTERvauseGlobal*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-apiningAppModuleini 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
undefinedmi; - 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):
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:
@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:
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_GUARDorqali yaratilganSozlamaliGuardsozlamani oldi (s=bor).new SozlamaliGuard()bilan yaratilgani —s=undefined,falseqaytardi va so'rov 403 oldi. Bir xil klass — ikki xil xulq. - Xususiyat modulidagi
APP_GUARDham global:IkkinchiGuardEksportModuleda 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_GUARDlaruseGlobalGuardsdan 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_GUARDni "shu modulning guard'i" deb o'ylash — klassik xato. Yaxshi amaliyot: global enhancer'larni bitta joyda —AppModuleda (yoki u import qiladigan maxsusilovamodulida) 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):
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:
APP_GUARD: true
new: falseKonteyner 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):
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 --> GXuddi 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:
- Qatlamlar tartibi qat'iy: middleware → guard → interceptor → pipe → handler. Shu sabab 415 (guard) validatsiyadan (pipe) oldin chiqadi, ETag (interceptor) javob yuborilishidan oldin qo'yiladi.
- 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:
ProblemDetailsFilterAPP_FILTERbilan ulangan, ustigamain.tsdaapp.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 darsidagiBaseExceptionFiltermisoli); - 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
- Testda himoyasiz ilova. Auth guard
main.tsdauseGlobalGuards(new AuthGuard(...))bilan ulangan; testlarAppModuledan 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. - DI'siz guard'ning sukut javobi. Guard bog'liqligi
undefinedbo'lgandatrueqaytarsa (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). - Xususiyat modulidagi kutilmagan global guard. Kimdir
EksportModulega "faqat eksport uchun" debAPP_GUARDqo'shadi — u butun API'ni sekinlashtiradi yoki yopadi. Himoya —APP_*faqatAppModuleda, review qoidasi. - Tartibga tayangan xavfsizlik. "Avval auth guard, keyin rol guard" — turli modullarda e'lon qilingan bo'lsa, tartib o'zgarishi rol guard'ini
req.usersiz 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())daGning bog'liqliklari —- Guard va pipe'dan qaysi biri oldin —
- Bir xatoni nechta filtr ushlaydi —
Yechim
- APP_GUARD —
@nestjs/coredan;{ provide: APP_GUARD, useClass: G }. - APP_INTERCEPTOR — xuddi shunday;
APP_PIPE,APP_FILTERham. - undefined —
newDI'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
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:
guard: JsonTanaGuard
guard: RolGuard
interceptor: Log
interceptor: VazifaSarlavhalari
pipe: SxemaPipe
HANDLER
interceptor (chiqish): VazifaSarlavhalari
interceptor (chiqish): LogRo'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
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:
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.
// 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):
--- 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):
$ 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);DeprecationvaLink—AppModule.configuredagieskirgan,req.originalUrlbilan;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
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.tsyokiapp.module.tsningprovidersida: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_GUARDvauseGlobalGuardsfarqi?", "Nest so'rov hayotidagi tartibni ayting", "xususiyat modulidagiAPP_GUARDqayerda ishlaydi?".
Xulosa
- Enhancer'lar — guard, interceptor, pipe, filtr; global ulash —
APP_*(DI bilan) yokiuseGlobal*(new, DI'siz). APP_*istalgan modulda e'lon qilinsa ham — butun ilova uchun; testda modul bilan birga keladi.newbilan 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 —AppModuledaAPP_*; 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
- NestJS hujjati — Guards (global guards,
APP_GUARD), Pipes (APP_PIPE), Interceptors (APP_INTERCEPTOR), Exception filters (APP_FILTER): https://docs.nestjs.com/guards , https://docs.nestjs.com/pipes , https://docs.nestjs.com/interceptors , https://docs.nestjs.com/exception-filters - NestJS hujjati — Request lifecycle: https://docs.nestjs.com/faq/request-lifecycle
- Kanon (IlmHamroh, 2026-10-27): lifecycle tajribasi (NestJS 12.2.0, Vitest ichida),
vazifalar-apitekshiruvi — tip/lint/Vitest 117/118/build, Testcontainers 10/10, jonli 9 so'rov × 3 rejim. Demo (APP_GUARDvauseGlobalGuards, xususiyat modulidagiAPP_GUARD), formatlangan fayllar bilan qayta qurilgan nusxa va jonli so'rovlar — biz ishga tushirdik (2026-11-02, NestJS 12.2.0, Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!