IlmHamroh
JavaScript Full-stack/30-qism. NestJS7/65-dars19 daqiqa
Mundarija (26)

NestJS request lifecycle: middleware, guard, interceptor, pipe va filter tartibi

Qisqacha: Nest'da so'rov qat'iy tartibda o'tadi: middleware → guard → interceptor (oldin) → pipe → handler → interceptor (keyin) → javob. Har bosqichda tartib "keng → tor": global (avval APP_* provider'lar, keyin useGlobal*), keyin controller, keyin route. Interceptor'ning "keyin" qismi teskari tartibda ishlaydi. Xato bo'lsa, interceptor'lar xato tarmog'idan o'tadi va faqat bitta — eng yaqin — exception filter javob beradi. Guard rad etsa, interceptor, pipe va handler umuman ishlamaydi.

Bu darsda

  • Nest'ning besh "g'ishti" — middleware, guard, interceptor, pipe, filter — qaysi savolga javob berishini bilasiz.
  • So'rovning to'liq yo'lini kanon tajribasining haqiqiy izi bilan o'qiysiz.
  • Global, controller va route darajalari qaysi tartibda ishlashini va APP_GUARD bilan useGlobalGuards farqini tushuntirasiz.
  • Xato bo'lganda qaysi filter javob berishini oldindan ayta olasiz.
  • vazifalar-api dagi har vazifa uchun to'g'ri mexanizmni tanlaysiz.

Oldin bilishingiz kerak: Bootstrap: main.ts va ilova sozlamalari, Middleware va request lifecycle, Global error handler, Controller'lar va route dekoratorlari.

O'tgan darsda vazifalar-api to'liq Nest'ga o'tdi. Lekin uning ichi hali 25-qism uslubida: kirish tekshiruvi handler ichida, sarlavhalar @Res({ passthrough: true }) orqali. Keyingi darslarda bular Nest'ning o'z mexanizmlariga ko'chadi. Bu dars — xarita: qaysi mexanizm qachon ishlaydi. Loyihaga kod qo'shilmaydi, faqat tajriba.

1. Nega bu kerak?

25-qismda so'rov hayoti oddiy edi: middleware'lar siz yozgan tartibda ishlardi (Middleware va request lifecycle). app.use(a); app.use(b) — avval a, keyin b. Xato bo'lsa — oxirgi 4 argumentli ishlovchi.

Nest'da qurilish bloklari ko'p: middleware, guard, interceptor, pipe, filter. Har biri global, controller yoki route darajasida qo'yilishi mumkin. Tartibni endi siz emas, freymvork belgilaydi. Uni bilmasangiz, g'alati xatolar chiqadi: "nega mening logim chiqmadi?", "nega pipe guard'dan oldin ishlamadi?", "nega global filtrim javob bermadi?".

O'xshatish: aeroport. Avval bino eshigi (middleware) — hammani kiritadi, sumkani skanerlaydi. Keyin pasport nazorati (guard) — "uchishga ruxsatingiz bormi?". Keyin samolyot ichi (interceptor) — parvoz boshida va oxirida xizmat. Bagaj tartibga solinadi (pipe). Uchishning o'zi — handler. Muammo bo'lsa — navbatchi xodim (filter), lekin faqat eng yaqindagisi.

2. Besh g'isht

2.1 Har biri — bitta savol

Mexanizm Savol Express'dagi o'xshashi
middleware "So'rovni oldindan tayyorlash kerakmi?" app.use(fn)
guard (himoyachi) "Bu so'rov handler'ga o'tsinmi?" auth middleware, 403
interceptor (ushlab qoluvchi) "Handler'dan oldin va keyin nima qilay?" res.on("finish"), javobni o'rash
pipe "Argument to'g'rimi, kerakli turdami?" Zod tekshir() handler boshida
exception filter (xato filtri) "Xatoni qanday javobga aylantiray?" 4 argumentli xato ishlovchi

Ta'riflar:

  • Middleware — Express middleware'ining aynan o'zi. U Nest'ni bilmaydi: qaysi controller, qaysi metod ishlashini ko'rmaydi. To'liq — Middleware Nest'da.
  • Guard — "ha yoki yo'q" deydigan klass. U qaysi handler chaqirilishini biladi va uning metama'lumotini (masalan @Rollar("admin")) o'qiy oladi. false qaytarsa — 403. To'liq — Guard'lar.
  • Interceptor — handler'ni o'rab oladi: oldin bir narsa qiladi, keyin javobni ko'radi yoki o'zgartiradi. Ichida RxJS oqimi ishlaydi. To'liq — Interceptor'lar va RxJS.
  • Pipe — har argumentni handler'ga berishdan oldin tekshiradi yoki o'zgartiradi ("7" → 7). To'liq — Pipe'lar.
  • Exception filter — o'tgan darsdagi ProblemDetailsFilter. To'liq — Exception filter'lar.

2.2 To'rt daraja

Middleware'dan boshqa hammasi to'rt joyda qo'yilishi mumkin:

ts
// 1) global, DI orqali — modul provider'i
providers: [{ provide: APP_GUARD, useClass: DiGuard }],

// 2) global, qo'lda — main.ts / ilova.ts da
app.useGlobalGuards(new OddiyGuard());

// 3) controller — klass ustida
@UseGuards(ControllerGuard)
@Controller("t")
class TController {
  // 4) route — metod ustida
  @UseGuards(RouteGuard)
  @Get(":id") ol() { /* … */ }
}

Pipe uchun beshinchi joy ham bor — bitta parametr: @Param("id", ParseIntPipe).

APP_GUARD va useGlobalGuards farqi: birinchisi — Nest yaratadigan provider, uning constructor'i boshqa provider'larni (ombor, config) ola oladi. Ikkinchisi — siz new bilan yaratgan tayyor obyekt, DI'dan tashqarida. O'tgan darsdagi new ProblemDetailsFilter(log) — ikkinchi tur. Uni APP_FILTER ga o'tkazish — Global enhancer'lar va bajarilish tartibi darsida.

2.3 Qaysi darajani tanlash

Daraja — "bu qoida qancha yo'lga tegishli" degan savolning javobi:

Daraja Qachon vazifalar-api dan misol
global butun ilovaga bitta qoida Problem Details filtri, auth guard
controller bitta resursning hamma yo'llariga /eksportlar ga alohida cheklov
route bitta metodga faqat POST /toplam uchun katta tana
parametr (pipe) bitta argumentga :id — musbat butun son

Qoida: qoidani eng tor mos darajaga qo'ying. Hamma narsa global bo'lsa, "bu yo'lga nega bu tekshiruv tushdi?" degan savolga javob topish qiyinlashadi. Hamma narsa route darajasida bo'lsa — takror va unutilgan metodlar paydo bo'ladi. Ikki chekka o'rtasidagi muvozanat — controller.

Bitta darajada bir nechta bo'lsa ham tartib aniq: @UseGuards(A, B) — avval A, keyin B, chapdan o'ngga. A rad etsa, B umuman chaqirilmaydi. Interceptor'larda @UseInterceptors(A, B) — A tashqi qavat: kirishda birinchi, chiqishda oxirgi. Shuning uchun arzon tekshiruvni chapga yozing: masalan, "tana JSON'mi" guard'i qimmat baza so'rovi qiladigan guard'dan oldin.

Interceptor'ni piyozga o'xshatish mumkin. Har qavat handler'ni o'raydi. So'rov tashqaridan ichkariga kiradi — global qavat birinchi. Javob ichkaridan tashqariga chiqadi — global qavat oxirgi. Shuning uchun global interceptor eng aniq vaqtni o'lchaydi: u boshqa hamma qavatlarni o'z ichiga oladi.

3. Kanon tajribasi: haqiqiy iz

3.1 Tajriba sharti

Kanon har darajaga har mexanizmdan bittadan qo'ydi. Har biri ishga tushganda o'z nomini ro'yxatga yozadi (yoz(...)). Kod qisqartirilgan:

ts
const guard = (nom: string, ruxsat = true): CanActivate => ({
  canActivate: () => {
    yoz(`guard: ${nom}`);
    return ruxsat;
  },
});
const interceptor = (nom: string): NestInterceptor => ({
  intercept: (_c, next) => {
    yoz(`interceptor (oldin): ${nom}`);
    return next.handle().pipe(tap({
      next: () => { yoz(`interceptor (keyin): ${nom}`); },
      error: () => { yoz(`interceptor (xato): ${nom}`); },
    }));
  },
});

@Controller("t")
@UseGuards(guard("controller"))
@UseInterceptors(interceptor("controller"))
@UsePipes(pipe("controller"))
@UseFilters(new Filtr("controller"))
class TController {
  @Get(":id")
  @UseGuards(guard("route"))
  @UseInterceptors(interceptor("route"))
  @UsePipes(pipe("route"))
  @UseFilters(new Filtr("route"))
  ol(@Param("id", yiqiluvchiPipe) id: string) {
    yoz("HANDLER");
    if (id === "boom") throw new Error("handler yiqildi");
    return { id };
  }

  @Post()
  @UseGuards(guard("route (rad)", false))
  yoz(@Body() b: unknown) { /* … */ }
}

Modulda APP_GUARD, APP_INTERCEPTOR, APP_PIPE, APP_FILTER, configure() da modul middleware'i; bootstrap'da app.use, useGlobalGuards, useGlobalInterceptors, useGlobalPipes, useGlobalFilters. yiqiluvchiPipe — param pipe: "xato" kelsa xato tashlaydi. Filtr har xatoga 418 va {"filtr": "<nomi>"} qaytaradi — qaysi filtr ishlaganini ko'rish uchun. Tajriba Vitest muhitida (Vite) yurgizilgan.

3.2 Muvaffaqiyatli so'rov

Birinchi so'rov hech qayerda yiqilmaydi. Izdagi har qator — bitta mexanizm ishga tushgan lahza. Qatorlarning tartibiga qarang, raqamlar shuning uchun qo'yilgan:

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

21 qatorni guruhlab o'qing:

  • 1–2. Avval middleware'lar: app.use bilan qo'yilgani, keyin modul middleware'i.
  • 3–6. Guard'lar: global → controller → route. Ikki global guard'dan avval APP_GUARD (DI orqali), keyin useGlobalGuards.
  • 7–10. Interceptor'larning "oldin" qismi — xuddi shu tartibda.
  • 11–15. Pipe'lar: global → controller → route → parametr. (param:id) — pipe qaysi argument ustida ishlayotgani. 15-qatordagi pipe nomi "yiqiladi", lekin bu so'rovda id — "5", u o'tkazib yubordi.
  • 16. Handler.
  • 17–20. Interceptor'larning "keyin" qismi — teskari: route → controller → global. Eng ichki o'rovchi birinchi ko'radi.
  • 21. Javob ketdi.

Kanonning bitta topilmasi 3–4 va 7–8 qatorlarda: DI bilan qo'yilgan global (APP_*) enhancer useGlobal* dan oldin ishlaydi.

sequenceDiagram
  participant M as Middleware
  participant G as Guard
  participant I as Interceptor
  participant P as Pipe
  participant H as Handler
  M->>G: global, modul
  G->>I: global, controller, route
  I->>P: oldin: global → route
  P->>H: global → param
  H-->>I: natija
  I-->>M: keyin: route → global

Diagrammadagi qaytuvchi strelkalarga qarang: javob interceptor'lardan teskari tartibda o'tadi. Kirishda global birinchi, chiqishda — oxirgi.

3.3 Pipe xatosi

Ikkinchi so'rovda id — "xato", parametr pipe'i uni rad etadi. Endi HANDLER qatori bormi-yo'qligiga va interceptor qatorlarida qaysi so'z turganiga qarang:

text
2) pipe xatosi GET /t/xato → 418 {"filtr":"route"}
  …
  14. pipe: route (param:id)
  15. pipe: route param (yiqiladi)
  16. interceptor (xato): route
  17. interceptor (xato): controller
  18. interceptor (xato): global (useGlobalInterceptors)
  19. interceptor (xato): global (APP_INTERCEPTOR)
  20. filter: route ← pipe rad etdi
  21. javob ketdi: 418

(1–13 qatorlar muvaffaqiyatli so'rovdagidek — qisqartirdik.)

Uch narsa. Birinchidan, HANDLER yo'q — pipe rad etdi, handler chaqirilmadi. Ikkinchidan, interceptor'lar xato tarmog'idan o'tdi (error callback'i), yana teskari tartibda. Demak pipe xatosi interceptor'lar "ichida" sodir bo'ladi — interceptor uni ko'radi va xohlasa o'zgartira oladi. Uchinchidan, faqat bitta filter ishladi — route. Controller va global filtrlar ham bor edi, lekin ular ishlamadi. Nest eng yaqin filtrni tanlaydi va to'xtaydi.

Handler xatosi (GET /t/boom) ham xuddi shunday: HANDLER dan keyin interceptor'larning xato tarmog'i va filter: route ← handler yiqildi.

3.4 Guard rad etdi

To'rtinchi so'rov POST /t ga boradi. U yerdagi route guard ataylab false qaytaradi. Iz qancha qisqarganiga qarang:

text
4) guard rad etdi POST /t → 418 {"filtr":"controller"}
   1. middleware: global (app.use)
   2. middleware: modul (MiddlewareConsumer)
   3. guard: global (APP_GUARD)
   4. guard: global (useGlobalGuards)
   5. guard: controller
   6. guard: route (rad)
   7. filter: controller ← Forbidden resource
   8. javob ketdi: 418

Atigi 8 qator. Route guard false qaytardi va zanjir shu yerda uzildi: interceptor ham, pipe ham, handler ham yo'q. Nest false ni ForbiddenException("Forbidden resource") ga aylantirdi — "Taqiqlangan resurs". Javob bergan filter — controller: POST metodida route filtri yo'q edi, eng yaqini controller'niki.

Bu tartibning ma'nosi bor: ruxsati yo'q so'rov uchun tanani tekshirish (pipe) yoki vaqtni o'lchash (interceptor) — behuda ish. Guard ularni oldindan to'xtatadi.

Tekshirib ko'ring: Global interceptor so'rov vaqtini o'lchaydi va logga yozadi. Guard so'rovni rad etdi. Bu so'rov logda bormi?

Javob

Yo'q. Guard interceptor'dan oldin ishlaydi — rad etilgan so'rov interceptor'gacha yetmaydi (tajribadagi 4-holat: 8 qatorda interceptor yo'q). Har so'rovni loglash kerak bo'lsa — middleware ishlating: u guard'dan oldin. vazifalar-api da so'rov logi shuning uchun app.use(sorovLogi(log)) — middleware.

4. Middleware va guard: kim nimani biladi

4.1 ExecutionContext

Middleware va guard ikkalasi ham handler'dan oldin ishlaydi. Unda farq nima? Farq — ko'rish doirasi. Demo loyihada ikkalasi bir so'rov haqida nima bilishini chiqardik (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0):

ts
class KimGuard implements CanActivate {
  canActivate(ctx: ExecutionContext) {
    const req = ctx.switchToHttp().getRequest<Request>();
    console.log(
      `guard: ${ctx.getType()} ${ctx.getClass().name}.` +
        `${ctx.getHandler().name} (${req.method} ${req.url})`,
    );
    return true;
  }
}

app.use((q: Request, _s: Response, n: NextFunction) => {
  console.log(
    `middleware: ${q.method} ${q.url}, route: ${String(q.route)}`,
  );
  n();
});

GET /vazifalar/7 so'rovi:

text
middleware: GET /vazifalar/7, route: undefined
guard: http VazifalarController.ol (GET /vazifalar/7)

Middleware faqat URL'ni ko'rdi: qaysi marshrut ishlashi hali noma'lum (route: undefined). Guard esa aniq biladi: kontekst turi http, klass VazifalarController, metod ol. Bu ma'lumotni ExecutionContext (bajarilish konteksti) beradi — guard, interceptor va filter oladigan obyekt. getHandler() va getClass() orqali ular metod va klass ustidagi dekoratorlarni o'qiy oladi: "bu yo'l ochiqmi?", "qaysi rol kerak?". Bu — Reflector va DiscoveryService darsining mavzusi.

ctx.getType() — http, rpc (mikroservis) yoki ws (WebSocket). Bitta guard uchala transportda ishlashi mumkin, shuning uchun so'rov switchToHttp() orqali olinadi — o'tgan darsdagi filtrdagi host.switchToHttp() ham shu sababdan.

4.2 Middleware xatosi qayerga boradi

Middleware Nest'ni bilmasa, uning xatosini kim ushlaydi? Demo: app.use middleware'i /a da, modul middleware'i (MiddlewareConsumer) /b da xato tashlaydi; global filtr 418 va xabarni qaytaradi:

text
GET /a → 418 {"filtr":"global","xabar":"app.use xatosi"}
GET /b → 418 {"filtr":"global","xabar":"modul middleware xatosi"}
GET /c → 200 {"handler":true}

Ikkala xato ham global filtrga yetdi. Nest Express'ning xato yo'liga o'z ishlovchisini ulaydi, shuning uchun app.use dagi xato ham filtrdan o'tadi. Lekin faqat global filtrdan: controller va route filtrlari middleware xatosini ko'rmaydi — o'sha paytda marshrut hali tanlanmagan. O'tgan darsdagi tanaXatosi topilmasi shu yo'lda sodir bo'lgan: express.json xatosi global filtrga yetguncha Nest uni "xaritalab" ulgurgan edi.

5. JS modelida so'rov yo'li

Tartibni kichik modelda takrorlaymiz — brauzerda ishlaydi. Har bosqich to'rt darajada (APP, global, controller, route); int> — interceptor oldin, int< — keyin, int! — xato tarmog'i:

js
const DARAJA = ["APP", "global", "controller", "route"];

function sorov(nom, { guardRad, pipeXato, handlerXato, filtrlar }) {
  const iz = [];
  try {
    iz.push("mw:global", "mw:modul");
    for (const d of DARAJA) {
      iz.push(`guard:${d}`);
      if (guardRad && d === "route") throw new Error("Forbidden");
    }
    for (const d of DARAJA) iz.push(`int>${d}`);
    try {
      for (const d of DARAJA) iz.push(`pipe:${d}`);
      if (pipeXato) throw new Error("pipe rad etdi");
      iz.push("HANDLER");
      if (handlerXato) throw new Error("handler yiqildi");
      for (const d of DARAJA.toReversed()) iz.push(`int<${d}`);
    } catch (x) {
      for (const d of DARAJA.toReversed()) iz.push(`int!${d}`);
      throw x;
    }
  } catch (x) {
    // faqat bitta filter — eng yaqini (route → controller → global)
    const f = ["route", "controller", "global"]
      .find((d) => filtrlar.includes(d));
    iz.push(`filter:${f} ← ${x.message}`);
  }
  console.log(`${nom}: ${iz.join(" ")}`);
}

const filtrlar = ["route", "controller", "global"];
sorov("1", { filtrlar });
sorov("2", { pipeXato: true, filtrlar });
sorov("4", { guardRad: true, filtrlar: ["controller", "global"] });

Konsolda:

text
1: mw:global mw:modul guard:APP guard:global guard:controller guard:route int>APP int>global int>controller int>route pipe:APP pipe:global pipe:controller pipe:route HANDLER int<route int<controller int<global int<APP
2: mw:global mw:modul guard:APP guard:global guard:controller guard:route int>APP int>global int>controller int>route pipe:APP pipe:global pipe:controller pipe:route int!route int!controller int!global int!APP filter:route ← pipe rad etdi
4: mw:global mw:modul guard:APP guard:global guard:controller guard:route filter:controller ← Forbidden

Uch qator — kanon izining qisqa shakli. Modelning tuzilishiga qarang: interceptor'lar ichki try ni o'raydi, shuning uchun pipe va handler xatosini ko'radi; guard xatosi esa tashqi try da — interceptor'ga yetmaydi. Haqiqiy Nest'da ham shunday: guard'lar interceptor zanjiridan tashqarida.

Model soddalashtirilgan: haqiqiy Nest'da interceptor xatoni "yutib" boshqa javob qaytara oladi, filter esa xato turiga qarab tanlanadi (@Catch(HttpException) faqat shu turni ushlaydi). toReversed — massivni teskari nusxalaydi (massiv saralash).

6. Qaysi vazifa — qaysi mexanizm

vazifalar-api dagi ishlarni joyiga qo'yamiz. Hozir ularning ko'pi 25-qism uslubida turibdi, keyingi darslarda shu jadval bo'yicha ko'chadi:

Vazifa Mexanizm Nega aynan u
sorovId, so'rov logi, helmet, CORS middleware hamma so'rovga, guard'dan oldin
JSON tana o'qish (16 KB) middleware handler'ni bilish shart emas
415 — tana JSON emas guard handler'ga o'tsinmi? — ha/yo'q
Auth (28-qism) guard metama'lumot (@Ochiq) kerak
Zod bilan body/params tekshiruvi pipe har argument alohida
ETag, Cache-Control, vaqt o'lchash interceptor javobni ko'rish kerak
Problem Details filter xatoni javobga aylantirish

Ikki qoida yordam beradi:

  1. Handler haqida bilish kerakmi? Kerak emas — middleware. Kerak (qaysi controller, qaysi dekorator) — guard yoki interceptor.
  2. Javobni ko'rish kerakmi? Kerak — interceptor. Faqat "o'tsinmi" — guard.

Masalan 415 tekshiruvi hozir jsonTanaKerak(req) — handler ichida, har metodda qo'lda. Guard bo'lsa, u handler'dan oldin ishlaydi va bitta joyda yoziladi. Kanon buni Zod va Standard Schema NestJS'da bosqichida qiladi.

6.1 Bitta so'rovni boshidan oxirigacha kuzatamiz

Jadvalni bitta haqiqiy holatda sinab ko'raylik. Mijoz POST /vazifalar yuboradi, tanasi JSON, lekin nom maydoni bo'sh. Hamma narsa yuqoridagi jadval bo'yicha joyiga ko'chgan, deb tasavvur qilamiz:

Qadam Mexanizm Nima bo'ladi
1 middleware sorovId beriladi, tana o'qiladi, log boshlanadi
2 guard tana JSON — o'tkazadi
3 interceptor (oldin) vaqt hisoblagichi ishga tushadi
4 pipe Zod: nom bo'sh — xato tashlaydi
5 interceptor (xato) xatoni ko'radi, lekin o'zgartirmaydi
6 filter Problem Details, status 422

422 — 25-qismdagi qoida: tana mazmuni noto'g'ri. Handler bu so'rovni umuman ko'rmaydi — 4-qadamda zanjir uzildi. Ombor ham, baza ham chaqirilmadi. Middleware'dagi log esa so'rovni yozib qo'ydi, chunki u guard'dan ham, pipe'dan ham oldin turadi.

Endi tanasi text/plain bo'lgan so'rovni o'ylang. U 2-qadamda — guard'da — 415 bilan to'xtaydi. 3–5-qadamlar bo'lmaydi: interceptor ham, pipe ham ishga tushmaydi. Javobni filter beradi. Shunday qilib, bitta jadval ikki savolga javob beradi: xato qayerda chiqdi va undan keyingi qaysi qadamlar bajarilmadi.

Tekshirib ko'ring: Har javobga X-Javob-Vaqti: 12ms sarlavhasini qo'shish kerak. Middleware'mi, guard'mi yoki interceptor'mi?

Javob

Interceptor — handler'dan oldin vaqtni oladi, keyin javob tayyor bo'lganda farqni hisoblaydi. Middleware ham qila oladi (res.on("finish")), lekin u sarlavhani javob yuborilgandan keyin qo'sha olmaydi — finish paytida sarlavhalar ketib bo'lgan. Guard esa javobni umuman ko'rmaydi.

7. Nega loyihaga kirmadi

Bu darsda vazifalar-api ga kod qo'shilmaydi. Tajriba — tushunish uchun: har mexanizm o'z darsida loyihaga keladi va o'sha paytda shu tartib kerak bo'ladi. Masalan, keyinroq global auth guard (KirishGuard) qo'shilganda kanon bitta haqiqiy xato topadi: idempotentlik middleware bo'lgani uchun guard'dan oldin ishlab, anonim so'rovning 401 javobini keshlab qo'ygan. Tartibni bilmasangiz, bunday xatoni topish qiyin. Bu topilma auth darslarida (Global auth guard) batafsil.

8. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Rad etilgan so'rovlar logda yo'q log interceptor'da, guard undan oldin so'rov logi — middleware
Global filtr javob bermadi route yoki controller filtri bor — eng yaqini ishladi filtrlarni bitta joyda saqlang
Pipe req.user ni ko'rmaydi deb o'ylash aslida guard pipe'dan oldin — ko'radi tartibni jadvaldan tekshiring
useGlobalGuards(new X()) da DI ishlamaydi new bilan yaratilgan, konteynerdan tashqarida APP_GUARD provider
Middleware handler metama'lumotini o'qiy olmaydi middleware Nest marshrutini bilmaydi guard yoki interceptor

9. Mashqlar

Mashqlar kurs/mashqlar/30/07-hayot-sikli/ da.

1-mashq (oson): tartibni toping

Bo'shliqlarni to'ldiring:

  • Guard'dan oldin ishlaydigan mexanizm —
  • Handler'dan keyin javobni ko'radigan mexanizm —
  • Guard false qaytarsa, Nest qaysi statusni beradi? —
  • Xato bo'lganda nechta filter javob beradi? —
  • Ikki global guard'dan qaysi biri oldin: APP_GUARD yoki useGlobalGuards? —
Yechim
  • middleware — Express darajasida, Nest marshrutidan oldin.
  • interceptor — "keyin" qismi; teskari tartibda.
  • 403 — ForbiddenException("Forbidden resource").
  • 1 — eng yaqini (route → controller → global).
  • APP_GUARD — kanon tajribasida 3-qator APP_GUARD, 4-qator useGlobalGuards.

2-mashq (o'rta): interceptor xatoni yutadi

Darsdagi modelga qutqaruvchi sozlamasini qo'shing: true bo'lsa, eng ichki (route) interceptor xatoni ushlab, o'rniga {"zaxira":true} javob beradi — filter ishlamaydi, qolgan interceptor'lar esa muvaffaqiyat tarmog'idan (int<) o'tadi. Pipe xatosi bilan ikki holatni chiqaring: qutqaruvchi: false va true. Ishora: ichki catch da if (qutqaruvchi) tekshiruvi.

Yechim
js
const DARAJA = ["APP", "global", "controller", "route"];

function sorov(nom, { pipeXato, qutqaruvchi }) {
  const iz = [];
  try {
    for (const d of DARAJA) iz.push(`int>${d}`);
    try {
      if (pipeXato) throw new Error("pipe rad etdi");
      iz.push("HANDLER");
    } catch (x) {
      if (!qutqaruvchi) {
        for (const d of DARAJA.toReversed()) iz.push(`int!${d}`);
        throw x;
      }
      iz.push("int!route → zaxira");
    }
    const tashqi = DARAJA.toReversed().slice(qutqaruvchi ? 1 : 0);
    for (const d of tashqi) iz.push(`int<${d}`);
    iz.push(qutqaruvchi ? 'javob {"zaxira":true}' : "javob");
  } catch (x) {
    iz.push(`filter:route ← ${x.message}`);
  }
  console.log(`${nom}: ${iz.join(" ")}`);
}

sorov("yutmaydi", { pipeXato: true, qutqaruvchi: false });
sorov("yutadi", { pipeXato: true, qutqaruvchi: true });

Konsolda:

text
yutmaydi: int>APP int>global int>controller int>route int!route int!controller int!global int!APP filter:route ← pipe rad etdi
yutadi: int>APP int>global int>controller int>route int!route → zaxira int<controller int<global int<APP javob {"zaxira":true}

Ikkinchi qatorda filter yo'q: route interceptor xatoni javobga aylantirdi va tashqi interceptor'lar uni oddiy javob deb ko'rdi. Haqiqiy Nest'da bu RxJS catchError operatori bilan qilinadi (Interceptor'lar va RxJS). Kuchli imkoniyat, lekin xavfli: xato jim yo'qolishi mumkin — logga yozmasdan yutmang.

3-mashq (qiyin): eng yaqin filter @Catch turi bilan

Haqiqiy Nest'da filter faqat o'z turidagi xatoni ushlaydi: @Catch(HttpException) oddiy TypeError ni o'tkazib yuboradi va navbat keyingi darajaga o'tadi. filtrTanla(xato, filtrlar) funksiyasini yozing: filtrlar — [{ daraja: "route", tur: HttpXato }, …] (tartib: route → controller → global; tur: null — hamma xato). Birinchi mos keladigan filter nomini qaytarsin, hech biri bo'lmasa — "nest sukut (500)". Sinov: HttpXato va TypeError bilan, ikki xil filtrlar ro'yxatida. Ishora: find va instanceof.

Yechim
js
class HttpXato extends Error {}

function filtrTanla(xato, filtrlar) {
  const f = filtrlar.find(
    (k) => k.tur === null || xato instanceof k.tur,
  );
  return f ? f.daraja : "nest sukut (500)";
}

const royxat1 = [
  { daraja: "route", tur: HttpXato },
  { daraja: "global", tur: null },
];
const royxat2 = [{ daraja: "controller", tur: HttpXato }];

console.log(filtrTanla(new HttpXato("404"), royxat1));
console.log(filtrTanla(new TypeError("x"), royxat1));
console.log(filtrTanla(new TypeError("x"), royxat2));

Konsolda:

text
route
global
nest sukut (500)

TypeError route filtriga mos kelmadi — navbat globalga o'tdi. Uchinchi holatda mos filter yo'q: Nest'ning o'z sukut ishlovchisi {"statusCode":500,"message":"Internal server error"} beradi. vazifalar-api filtri @Catch() — argumentsiz, ya'ni tur: null: u hamma xatoni oladi va bunday "teshik" qolmaydi.

10. Real ishda

  • Debug qilishda birinchi savol: "bu kod qaysi bosqichda?". Javob ko'pincha muammoni o'zi ochadi.
  • Jamoa qoidasi: auth — guard, validatsiya — pipe, log va sarlavha — interceptor yoki middleware, xato formati — bitta global filter. Har bir qatlam bitta ish.
  • Kod review: "bu tekshiruv nega handler ichida?" — guard yoki pipe'ga ko'chirish taklifi tez-tez uchraydi.
  • Intervyu: "Nest request lifecycle tartibini ayting" — eng ko'p beriladigan Nest savollaridan biri. Qo'shimcha savol: "guard va middleware farqi nima?" (guard ExecutionContext orqali handler'ni biladi).

Xulosa

  • Tartib: middleware → guard → interceptor (oldin) → pipe → handler → interceptor (keyin, teskari) → javob.
  • Har bosqichda: global (APP_*, keyin useGlobal*) → controller → route; pipe'da yana parametr darajasi.
  • Guard rad etsa — 403, interceptor/pipe/handler ishlamaydi; filter — eng yaqini.
  • Pipe va handler xatosini interceptor xato tarmog'ida ko'radi; javobni faqat bitta filter beradi.
  • Har vazifaga mexanizm: "handler'ni bilish kerakmi?" va "javobni ko'rish kerakmi?" degan savollar bilan tanlanadi.

Keyingi dars: DI va IoC konteyner — Nest konteyneri bog'liqliklarni qanday "ko'rishini" design:paramtypes orqali, tipik xatolarini esa haqiqiy matnlari bilan ko'ramiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
NestJS request lifecycle: middleware, guard, interceptor, pipe va filter tartibi — IlmHamroh