Mundarija (26)
- Bu darsda
- 1. Nega bu kerak?
- 2. HttpException oilasi
- 2.1 Tayyor xatolar
- 2.2 Problem Details bilan farq
- 3. O'z filtringiz
- 3.1 @Catch va ExceptionFilter
- 3.2 BaseExceptionFilter dan meros
- 3.3 Qaysi filtr ushlaydi
- 4. ORM xatolari qaysi qatlamda aylansin
- 4.1 Ikki yo'l
- 4.2 Kanon tanlovi — ombor qatlami
- 5. Hujumchi nigohi
- 6. Ko'p uchraydigan xatolar
- 7. Mashqlar
- 1-mashq (oson): to'ldiring
- 2-mashq (o'rta): filtr tanlash modeli
- 3-mashq (qiyin): Problem Details yasovchi
- 8. Loyiha qadami: Problem Details filtri — APP_FILTER
- 8.1 Nima o'zgaradi
- 8.2 src/umumiy/problem.filter.ts
- 8.3 Ulash
- 8.4 Tekshiruv
- 9. Real ishda
- Xulosa
- Manbalar
Exception filter'lar NestJS'da: HttpException, @Catch, ArgumentsHost va Problem Details
Qisqacha: Handler, pipe, guard yoki interceptor xato tashlasa, uni exception filter (xato filtri) ushlaydi va javobga aylantiradi. Nest'ning o'z filtri
HttpExceptionni (NotFoundException,ConflictException…){ message, error, statusCode }ga aylantiradi, qolgan har qanday xatoni —500 Internal server errorga. O'z filtringiz —@Catch(XatoKlassi)+ExceptionFilter.catch(xato, host);host.switchToHttp()—reqvares.@Catch()argumentsiz — hamma xato. Eng yaqin filtr ushlaydi: metod → controller → global. NestJS 12 daHttpExceptiongaerrorCodeqo'shildi.vazifalar-apining Problem Details filtri global —APP_FILTERbilan, DI orqali logger oladi.
Bu darsda
- Nest'ning o'rnatilgan
HttpExceptionoilasi qanday javob berishini haqiqiy chiqishda ko'rasiz. @Catch,ExceptionFilter,ArgumentsHostbilan o'z filtringizni yozasiz.- Filtrlar qaysi tartibda tanlanishini va
BaseExceptionFilterdan qanday meros olishni bilasiz. - ORM xatolarini (Prisma,
pg) qaysi qatlamda HTTP xatoga aylantirish kerakligini tushuntira olasiz. vazifalar-apining Problem Details filtriniAPP_FILTERga o'tkazasiz.
Oldin bilishingiz kerak: Xato javoblari: Problem Details, Xato turlari va custom error iyerarxiyasi, Global error handler, Interceptor'lar va RxJS.
O'tgan to'rt darsda so'rov hayotining har qatlamida xato tashladik. Hammasi bitta joyga keladi — filtrga:
| Qatlam | Bizdagi misol | Status | Kim javobga aylantiradi |
|---|---|---|---|
| guard (himoyachi) | Content-Type: text/plain |
415 | filtr |
| interceptor (ushlab qoluvchi) | timeout(100) |
503 | filtr |
| pipe | Zod sxemasi rad etdi | 422 | filtr |
| handler / xizmat | vazifa topilmadi | 404 | filtr |
| istalgan joy | kutilmagan TypeError |
500 | filtr |
Oxirgi ustun hamma qatorda bir xil. Xato qayerda tug'ilmasin, mijozga javobni bitta qatlam — exception filter (xato filtri) — yozadi. Shuning uchun javob shakli ham bitta bo'la oladi.
1. Nega bu kerak?
25-qismda Express'ning global xato ishlovchisini yozgan edik (Global error handler): (err, req, res, next) to'rt argumentli middleware. U har xatoni Problem Details javobiga aylantiradi: kutilgan xato (IlovaXatosi) — o'z statusi bilan, kutilmagani — 500 va tafsilotsiz.
Nest'da shu ishni filtr qiladi. Bootstrap darsida ProblemDetailsFilter ni app.useGlobalFilters(new ProblemDetailsFilter(...)) bilan uladik — ishlaydi. Lekin ikki savol qoldi:
- Nest'ning o'z xatolari (
NotFoundExceptionva boshqalar) qanday ko'rinishda? Kutubxonalar ularni tashlaydi — bizning filtr ularni to'g'ri aylantiradimi? - Filtr logger'ni qanday oladi? Hozir u constructor argumenti —
newbilan qo'lda beriladi; DI'dan emas.
O'xshatish: kasalxonaning qabul bo'limi. Qaysi bo'limda nima bo'lmasin (jarrohlik, terapiya, laboratoriya), bemor qabulga keladi va u yerda bir xil shakldagi hujjat oladi. Filtr — qabul bo'limi: xato qayerda tug'ilgani emas, mijozga bir xil javob muhim.
2. HttpException oilasi
2.1 Tayyor xatolar
Nest'da har HTTP xato statusi uchun klass bor: BadRequestException (400), UnauthorizedException (401), ForbiddenException (403), NotFoundException (404), ConflictException (409), UnprocessableEntityException (422), ServiceUnavailableException (503) va boshqalar. Hammasi HttpException dan meros oladi.
Demo controller'da ularni tashlab, sukut filtr javobini yozib oldik (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0). Chiqishda har qatorning tana shakliga qarang — qaysi maydonlar bor, qaysisi yo'q:
@Get('a') a() { throw new NotFoundException(); }
@Get('b') b() { throw new NotFoundException("Vazifa 7 yo'q"); }
@Get('c') c() {
throw new ConflictException('Takror vazifa', {
errorCode: 'TAKROR_MATN',
});
}
@Get('d') d() {
throw new BadRequestException(["matn bo'sh", 'limit > 100']);
}
@Get('e') e() {
throw new HttpException(
{ xabar: "o'zimizning tana", kod: 42 }, HttpStatus.I_AM_A_TEAPOT,
);
}
@Get('f') f() { throw new TypeError('x is undefined'); }GET /a → 404 {"message":"Not Found","statusCode":404}
GET /b → 404 {"message":"Vazifa 7 yo'q","error":"Not Found","statusCode":404}
GET /c → 409 {"message":"Takror vazifa","error":"Conflict","statusCode":409,"errorCode":"TAKROR_MATN"}
GET /d → 400 {"message":["matn bo'sh","limit > 100"],"error":"Bad Request","statusCode":400}
GET /e → 418 {"xabar":"o'zimizning tana","kod":42}
GET /f → 500 {"statusCode":500,"message":"Internal server error"}(Bu demoda global filtr har xatoni logga ham yozgan — o'sha qatorlarni tushirib qoldirdik.) Olti holatdan olti saboq:
/a— xabarsiz:message— status nomi,errormaydoni yo'q./b— xabar bilan:message— sizning matn,error— status nomi. Ya'ni javob shakli argumentga qarab o'zgaradi — mijoz ikkala shaklni ham kutishi kerak./c— NestJS 12 yangiligi:errorCodesozlamasi javobga"errorCode"maydonini qo'shadi. Mashina o'qiydigan xato kodi — 25-qismdagi Problem Detailskodmaydonining Nest varianti./d— massiv:ValidationPipexatolari shunday ko'rinadi (ValidationPipe sozlamalari)./e— obyekt berilsa, u to'liq tana bo'ladi./f—HttpExceptionbo'lmagan har qanday xato — 500, xabarsiz. Nest tafsilotni mijozga bermaydi — to'g'ri xulq.
2.2 Problem Details bilan farq
Nest formati — { message, error, statusCode }. Bu standart emas, Nest'ning o'z kelishuvi. vazifalar-api esa 25-qismdan beri RFC 9457 Problem Details qaytaradi: type, title, status, detail, instance va application/problem+json. Frontend shu formatni kutadi. Shuning uchun bizga o'z filtrimiz kerak.
Tekshirib ko'ring: Kutubxona
throw new UnauthorizedException()qildi. Sukut Nest filtri bilan mijoz qanday tana oladi?
Javob
{"message":"Unauthorized","statusCode":401} — /a holatidagidek: xabarsiz xatoda message — status nomi va error maydoni yo'q. Mijoz kodi error maydoniga tayansa — undefined oladi. Bitta API'da bitta xato shakli bo'lishi uchun ham o'z filtri kerak.
3. O'z filtringiz
3.1 @Catch va ExceptionFilter
Demo: domen xatosi — TopilmadiXatosi (HttpException emas, oddiy Error) — uchun filtr:
class TopilmadiXatosi extends Error {
constructor(readonly id: number) {
super(`Vazifa ${String(id)} topilmadi`);
}
}
@Catch(TopilmadiXatosi)
class TopilmadiFiltri implements ExceptionFilter {
constructor(private readonly adapter: HttpAdapterHost) {}
catch(x: TopilmadiXatosi, host: ArgumentsHost) {
const { httpAdapter } = this.adapter;
const ctx = host.switchToHttp();
httpAdapter.reply(ctx.getResponse(), {
type: 'about:blank', title: 'Not Found',
status: 404, detail: x.message,
}, 404);
}
}
@Get('g') @UseFilters(TopilmadiFiltri) g() {
throw new TopilmadiXatosi(7);
}
@Get('h') h() { throw new TopilmadiXatosi(8); }@Catch(TopilmadiXatosi)— filtr qaysi xato klasslarini ushlaydi (bir nechtasi — vergul bilan).@Catch()argumentsiz — hammasini.catch(xato, host)—ArgumentsHost— guard'dagiExecutionContextning "kichik akasi":switchToHttp()bilangetRequest()/getResponse().HttpAdapterHost— Express yoki Fastify'dan qat'iy nazar javob yuborish (httpAdapter.reply). Filtr platformaga bog'lanmaydi (Fastify adapter bilan NestJS).@UseFilters(...)— filtrni metod yoki controller'ga ulaydi.
GET /g → 404 {"type":"about:blank","title":"Not Found","status":404,"detail":"Vazifa 7 topilmadi"}
[global filtr] TopilmadiXatosi
GET /h → 500 {"statusCode":500,"message":"Internal server error"}/g — bizning filtr ushladi, Problem Details shaklida. /h — xuddi shu xato, lekin filtr ulanmagan: global filtrga yetdi va u uni tanimadi — 500. Domen xatosini HTTP'ga aylantirish faqat filtr ulangan joyda ishlaydi — shuning uchun bunday filtr odatda global bo'ladi.
3.2 BaseExceptionFilter dan meros
Demo'dagi global filtr Nest'ning sukut xulqini saqlab, faqat log qo'shgan:
@Catch()
class HammasiFiltri extends BaseExceptionFilter {
override catch(x: unknown, host: ArgumentsHost) {
console.log(` [global filtr] ` +
`${x instanceof Error ? x.constructor.name : typeof x}`);
super.catch(x, host);
}
}
app.useGlobalFilters(
new HammasiFiltri(app.get(HttpAdapterHost).httpAdapter),
);super.catch(...) — Nest'ning o'z filtri: HttpException → uning tanasi, qolgan → 500. "Faqat log qo'shmoqchiman" degan holat uchun qulay. BaseExceptionFilter ga httpAdapter kerak — useGlobalFilters da uni qo'lda beramiz (DI yo'q).
3.3 Qaysi filtr ushlaydi
Bir nechta filtr ulangan bo'lsa, eng yaqini — xato bitta filtrga tushadi, zanjir yo'q:
flowchart TB
X["xato tashlandi"] --> M{"metod filtri<br/>@Catch mos?"}
M -- "ha" --> J["javob"]
M -- "yo'q" --> C{"controller filtri<br/>mos?"}
C -- "ha" --> J
C -- "yo'q" --> G{"global filtr<br/>mos?"}
G -- "ha" --> J
G -- "yo'q" --> N["Nest sukut filtri<br/>(500 yoki HttpException)"]Kanon lifecycle tajribasida bu ko'rinadi (kurs tajribasi, NestJS 12.2.0, Vitest ichida; har filtr 418 va o'z nomini qaytaradi):
2) pipe xatosi GET /t/xato → 418 {"filtr":"route"}
…
20. filter: route ← pipe rad etdi
4) guard rad etdi POST /t → 418 {"filtr":"controller"}
…
7. filter: controller ← Forbidden resourcePipe xatosini metod filtri ushladi, global va controller filtrlari ishlamadi. To'rtinchi holatda POST /t marshrutida metod filtri yo'q edi — xatoni controller filtri oldi. Muhim nuqta: xato qaysi qatlamda tug'ilgani (pipe, guard, handler) muhim emas — filtr marshrutga qarab tanlanadi.
Filtr ichida ham xato tashlansa — uni Nest'ning sukut filtri oladi (500). Filtr kodi oddiy va xatosiz bo'lishi kerak.
4. ORM xatolari qaysi qatlamda aylansin
4.1 Ikki yo'l
Bazada noyoblik buzilsa, Prisma PrismaClientKnownRequestError (P2002) tashlaydi, pg — DatabaseError (23505). Mijozga bu 409 Takror vazifa bo'lishi kerak. Qayerda aylantiramiz?
| Yo'l | Qanday | Kamchilik |
|---|---|---|
| Filtrda | @Catch(Prisma.PrismaClientKnownRequestError) — P2002 → 409 |
filtr ORM'ni biladi; boshqa ORM — yana filtr |
| Ombor (repository) qatlamida | ombor ORM xatosini KonfliktXatosi ga o'raydi |
har omborda alohida |
Nest hujjatlarida va blog'larda birinchi yo'l ko'p uchraydi: "Prisma exception filter". U tez yoziladi.
4.2 Kanon tanlovi — ombor qatlami
vazifalar-api da uchta ombor bor: xotira (XotiraOmbori), pg (PgOmbori) va Prisma (PrismaOmbori) — 26–27-qismlardan. Ularning hammasi bitta VazifaOmbori interfeysini bajaradi (Repository naqshi va qatlamlar). Takror matn uchun hammasi bir xil xato tashlaydi — KonfliktXatosi (kod: "takror-matn"). Prisma omboridan parcha (kanon kodi, bo'lingan):
/** Postgres SQLSTATE: adapter xatoni PrismaClientKnownRequestError
* ichiga o'raydi — asl kodni meta'dan olamiz */
function sqlHolat(x: unknown): string | undefined {
if (!(x instanceof Prisma.PrismaClientKnownRequestError)) {
return undefined;
}
const sabab = (x.meta?.["driverAdapterError"] as
{ cause?: { originalCode?: string } } | undefined)?.cause;
return sabab?.originalCode;
}
function takrorXatosi(x: unknown): unknown {
const kod = sqlHolat(x);
// 23P01 — EXCLUDE (matn noyobligi), 23505 — eski UNIQUE indeks
if (kod === "23P01" || kod === "23505") {
return new KonfliktXatosi("Bu vazifa ro'yxatda allaqachon bor", {
kod: "takror-matn", sarlavha: "Takror vazifa", cause: x,
});
}
return x;
}Natija: filtr faqat IlovaXatosi ni biladi — ORM, Postgres kodlari, P2002 — hech biri. Saqlash rejimi almashsa (SAQLASH=xotira|postgres|prisma), HTTP javobi o'zgarmaydi. cause: x — asl xato yo'qolmaydi, logda ko'rinadi (Xato turlari, error.cause).
Qoida: domen xatosi — ombor/xizmatda, HTTP shakli — filtrda. Filtr "Postgres nima ekanini" bilmasa — yaxshi.
5. Hujumchi nigohi
- Stack va ichki xabar oqishi.
res.status(500).json({ error: err.message, stack: err.stack })— fayl yo'llari, SQL so'rovi, kutubxona versiyalari mijozga ketadi. Himoya — 500 da faqat umumiydetailva so'rov ID'si; tafsilot — logga (kanon filtri shunday). - Baza xato matni.
duplicate key value violates unique constraint "foydalanuvchilar_email_key"— jadval va ustun nomlari, "bu email ro'yxatda bor" degan ma'lumot. Himoya — ombor qatlamida domen xatosiga aylantirish; email uchun javob matnini "ro'yxatdan o'tish xatosi" kabi umumiy qilish (28-qismdagi hisob sanash himoyasi). - Filtrdagi xato. Filtr
xato.response.messageni tekshirmay o'qisa vaxato—nullyoki satr bo'lsa — filtrning o'zi yiqiladi va javob — Nest'ning sukut 500 i, logsiz. Himoya — filtrdainstanceoftekshiruvlari vaunknownturi (kanon filtricatch(xato: unknown, ...)). - Yarim yuborilgan javob. Oqim (stream) yoki CSV eksport yarmida xato bo'lsa — sarlavhalar allaqachon ketgan;
res.status(500)ikkinchi xatoni beradi. Himoya —res.headersSenttekshiruvi: faqat log va ulanishni yopish.
6. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| Domen xatosi 500 bo'ldi | filtr ulanmagan yoki @Catch mos emas |
global filtr yoki @Catch(Klass) |
Ba'zi javoblarda error maydoni yo'q |
new NotFoundException() xabarsiz |
o'z filtri bilan bir xil shakl |
Filtrda logger undefined |
useGlobalFilters(new F()) — DI yo'q |
APP_FILTER |
Cannot set headers after they are sent |
javob yarmida xato | res.headersSent tekshiruvi |
| Filtr ishlamaydi, sukut javob | filtr ichida xato | filtrni unknown bilan, tekshiruvlar bilan |
| Ikki filtr ham ishladi deb kutildi | faqat eng yaqin filtr | bitta filtrda hamma holat |
7. Mashqlar
Mashqlar kurs/mashqlar/30/24-filtrlar/ da.
1-mashq (oson): to'ldiring
- Hamma xatoni ushlaydigan filtr —
@() - Filtr metodining ikkinchi argumenti turi —
- 409 uchun tayyor xato —
new ("Takror") - NestJS 12 da mashina o'qiydigan kod —
{ : "TAKROR_MATN" } - DI bilan global filtr tokeni —
Yechim
- Catch — argumentsiz — hamma xato;
@Catch(A, B)— faqat shular. - ArgumentsHost —
switchToHttp().getRequest()/getResponse(). - ConflictException —
HttpExceptionoilasidan. - errorCode — javobda
"errorCode"maydoni. - APP_FILTER —
useGlobalFilters(new …)dan farqli, DI bilan.
2-mashq (o'rta): filtr tanlash modeli
Filtrlar ro'yxati — { nom, ushlaydi: [Klass, ...] }; bo'sh ushlaydi — hamma xato. filtrTop(qatlamlar, xato) funksiyasini yozing: qatlamlar — [metodFiltrlari, controllerFiltrlari, globalFiltrlar] (har biri massiv). Birinchi mos kelgan filtr nomini qaytarsin, hech biri mos kelmasa — "nest-sukut". Moslik — instanceof (meros ham hisoblanadi). Ishora: find, some (Massivda qidirish va tekshirish).
Yechim
class IlovaXatosi extends Error {}
class TopilmadiXatosi extends IlovaXatosi {}
function filtrTop(qatlamlar, xato) {
for (const qatlam of qatlamlar) {
const f = qatlam.find((f) => f.ushlaydi.length === 0
|| f.ushlaydi.some((K) => xato instanceof K));
if (f) return f.nom;
}
return "nest-sukut";
}
const metod = [{ nom: "topilmadi", ushlaydi: [TopilmadiXatosi] }];
const global = [{ nom: "problem", ushlaydi: [IlovaXatosi] }];
console.log(filtrTop([metod, [], global], new TopilmadiXatosi()));
console.log(filtrTop([[], [], global], new TopilmadiXatosi()));
console.log(filtrTop([metod, [], global], new TypeError("x")));
const hammasi = [{ nom: "hammasi", ushlaydi: [] }];
console.log(filtrTop([[], [], hammasi], new TypeError("x")));Konsolda:
topilmadi
problem
nest-sukut
hammasiIkkinchi qator — meros: TopilmadiXatosi ham IlovaXatosi, global filtr uni ushladi. Uchinchisi — TypeError hech qaysi @Catch ga mos emas: Nest'ning sukut filtri (500). Shuning uchun kanon filtri @Catch() — argumentsiz: kutilmagan xatolar ham Problem Details shaklida chiqsin.
3-mashq (qiyin): Problem Details yasovchi
problem(xato, so'rov) funksiyasini yozing — kanon filtrining mantiqi, toza funksiya sifatida. Kirish: xato — { holat, xabar, kod } (bizning xato), { getStatus, message } (Nest HttpException modeli) yoki boshqa narsa. Chiqish: { status, tana }, tana — type, title, status, detail, instance. Qoidalar: bizning xato — o'z statusi va kod dan type (https://vazifalar-api.example/xatolar/<kod>); HttpException < 500 — status saqlanadi, type: "about:blank"; qolgan — 500 va detail: "Kutilmagan xato", asl xabar chiqmasin. title uchun kichik jadval (404, 409, 500) yetadi.
Yechim
const NOMLAR = {
404: "Not Found", 409: "Conflict", 500: "Internal Server Error",
};
function problem(xato, sorov) {
const instance = sorov.originalUrl;
if (xato?.holat !== undefined) {
const type = xato.kod
? `https://vazifalar-api.example/xatolar/${xato.kod}`
: "about:blank";
return {
status: xato.holat,
tana: { type, title: NOMLAR[xato.holat], status: xato.holat,
detail: xato.xabar, instance },
};
}
const nest = typeof xato?.getStatus === "function";
if (nest && xato.getStatus() < 500) {
const s = xato.getStatus();
return {
status: s,
tana: { type: "about:blank", title: NOMLAR[s], status: s,
detail: xato.message, instance },
};
}
return {
status: 500,
tana: { type: "about:blank", title: NOMLAR[500], status: 500,
detail: "Kutilmagan xato", instance },
};
}
const s = { originalUrl: "/v1/vazifalar/7" };
const holatlar = [
{ holat: 409, xabar: "Takror", kod: "takror-matn" },
{ getStatus: () => 404, message: "Cannot GET /yoq" },
new TypeError("ombor.top is not a function"),
];
for (const x of holatlar) console.log(JSON.stringify(problem(x, s)));Konsolda:
{"status":409,"tana":{"type":"https://vazifalar-api.example/xatolar/takror-matn","title":"Conflict","status":409,"detail":"Takror","instance":"/v1/vazifalar/7"}}
{"status":404,"tana":{"type":"about:blank","title":"Not Found","status":404,"detail":"Cannot GET /yoq","instance":"/v1/vazifalar/7"}}
{"status":500,"tana":{"type":"about:blank","title":"Internal Server Error","status":500,"detail":"Kutilmagan xato","instance":"/v1/vazifalar/7"}}Uchinchi qatorda ombor.top is not a function — yo'q. Kutilmagan xato matni ichki tuzilmani ochadi; u faqat logga boradi. Kanon filtri qo'shimcha ravishda Nest router'ining 404 xabarini ("Cannot GET /yoq") o'zimizning "bunday manzil yo'q" xabariga almashtiradi.
8. Loyiha qadami: Problem Details filtri — APP_FILTER
8.1 Nima o'zgaradi
Hozir filtr ilova.ts da yaratiladi: app.useGlobalFilters(new ProblemDetailsFilter((_id, xato) => { joriyLog(log).error(...) })) — log funksiyasi constructor argumenti sifatida qo'lda beriladi. Endi filtr DI'dan LOG tokeni orqali logger oladi va AppModule da APP_FILTER bilan ulanadi.
Qo'shimcha foyda: testlar AppModule.forRoot(...) dan ilova yasasa, filtr ham o'z-o'zidan ulanadi — useGlobalFilters ni testda takrorlash shart emas.
8.2 src/umumiy/problem.filter.ts
Kanon faylida qatorlar uzun edi — bo'ldik va izohlarni qisqartirdik (kod ma'nosi aynan); nusxani kanon nusxasida tsc --noEmit, ESLint va Vitest bilan tekshirdik.
// problem.filter.ts — global exception filter: HAMMA xato bir
// joyda Problem Details'ga aylanadi (25-qism xato-ishlovchi.ts
// ning Nest shakli). @Catch() — argumentsiz: har qanday xato
// (IlovaXatosi, Nest HttpException, kutilmagan TypeError…).
// Global — APP_FILTER (DI bilan: LOG tokeni)
import {
Catch, HttpException, Inject, NotFoundException,
} from "@nestjs/common";
import type { ArgumentsHost, ExceptionFilter } from "@nestjs/common";
import type { Request, Response } from "express";
import { IlovaXatosi } from "./xatolar.ts";
import { problemYasa, problemYubor } from "./problem.ts";
import { joriyLog } from "./log.ts";
import type { Log } from "./log.ts";
import { LOG } from "../tokenlar.ts";
@Catch()
export class ProblemDetailsFilter implements ExceptionFilter {
constructor(@Inject(LOG) private readonly log: Log) {}
/** to'liq xato (stack bilan) — faqat logga; sorovId joriy so'rov
* logger'idan (AsyncLocalStorage) */
private logla(xato: unknown): void {
joriyLog(this.log).error({ err: xato }, "kutilmagan xato");
}
catch(xato: unknown, host: ArgumentsHost): void {
const http = host.switchToHttp();
const req = http.getRequest<Request>();
const res = http.getResponse<Response>();
if (res.headersSent) {
// javob yarmida — status o'zgartirib bo'lmaydi: log va
// ulanishni yopamiz
this.logla(xato);
res.end();
return;
}
const umumiy = {
instance: req.originalUrl, sorovId: req.sorovId,
};
if (xato instanceof IlovaXatosi) {
res.set(xato.httpSarlavhalar);
problemYubor(res, problemYasa(xato.holat, {
...umumiy, kod: xato.kod, sarlavha: xato.sarlavha,
detail: xato.message, xatolar: xato.xatolar,
}));
return;
}
// Nest router'i mos marshrut topmasa NotFoundException ("Cannot
// GET /yoq") tashlaydi — o'zimizning xabar (Express'dagidek)
if (xato instanceof NotFoundException) {
problemYubor(res, problemYasa(404, {
...umumiy,
detail: `${req.method} ${req.path} — bunday manzil yo'q`,
}));
return;
}
// Boshqa Nest HttpException'lari (kutubxonalar tashlaydi) —
// status saqlanadi, 5xx tafsiloti yashiriladi
if (xato instanceof HttpException && xato.getStatus() < 500) {
problemYubor(res, problemYasa(xato.getStatus(), {
...umumiy, detail: xato.message,
}));
return;
}
// Kutilmagan (dasturchi xatosi, DB uzilishi…): tafsilot
// mijozga BERILMAYDI — faqat logga
this.logla(xato);
problemYubor(res, problemYasa(500, {
...umumiy,
detail: "Kutilmagan xato. sorovId bilan murojaat qiling.",
}));
}
}Avvalgi holatdan farq:
- Constructor —
@Inject(LOG) private readonly log: Log. AvvalXatoLogituridagi funksiya edi, sukut qiymaticonsole.errorbilan. EndiLOG— globalBogliqliklarModuledagi pino logger (Custom provider'lar). logla(xato)—joriyLog(this.log)so'rov kontekstidagi bola logger'ni oladi (undasorovIdbor — 25-qismdagi AsyncLocalStorage). Shuning uchunsorovIdni argument qilib uzatish shart emas.catchning mantiqi — 3-mashqdagi tartib: bizning xato → Nest 404 → boshqaHttpException< 500 → qolgan hammasi 500.
8.3 Ulash
app.module.ts da providers ga uchinchi global enhancer qo'shiladi (importga APP_FILTER va ProblemDetailsFilter):
// global enhancer'lar DI bilan: 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 },
],ilova.ts dan useGlobalFilters qatori va endi kerak bo'lmagan importlar o'chadi:
-import { ProblemDetailsFilter } from "./umumiy/problem.filter.ts";
-import { joriyLog, logYarat, sorovLogi } from "./umumiy/log.ts";
+import { logYarat, sorovLogi } from "./umumiy/log.ts";
…
- app.useGlobalFilters(new ProblemDetailsFilter((_id, xato) => {
- joriyLog(log).error({ err: xato }, "kutilmagan xato");
- }));
+ // Problem Details filtri — AppModule'da APP_FILTER (DI bilan)test/marshrutlar.test.ts — ilovani AppModule.forRoot dan yasaydi va filtrni o'zi ulardi. Endi filtr modul ichida, va eski chaqiruv tur xatosi ham beradi (constructor argumenti Log bo'ldi):
-import { ProblemDetailsFilter }
- from "../src/umumiy/problem.filter.ts";
…
app = await NestFactory.create(SinovModuli, { logger: false });
- app.useGlobalFilters(new ProblemDetailsFilter(() => undefined));
await app.init();8.4 Tekshiruv
npm run tip, npm run lint, npm test — toza (Postgres'siz: Tests 75 passed | 6 skipped (81)). marshrutlar.test dagi 405 tekshiruvlari Problem Details javobini kutadi — u o'tgani filtr testda ham ulanganini ko'rsatadi. Hali commit qilmang.
9. Real ishda
- Har jiddiy Nest loyihasida bitta global filtr bor: xato formati (ko'pincha RFC 9457 yoki kompaniya standarti), log, Sentry'ga yuborish.
- "Prisma exception filter" — mashhur naqsh; review'da savol: "boshqa ORM yoki keshdan kelgan xato ham shu filtrdan o'tadimi?".
- Intervyu: "
HttpExceptionva oddiyErrorfarqi Nest'da?", "filtrlar qaysi tartibda tanlanadi?", "500 javobida nima bo'lishi kerak?".
Xulosa
- Filtr — xatoni javobga aylantiradi:
@Catch(Klass)+ExceptionFilter.catch(xato, host);@Catch()— hammasi. - Nest sukut filtri:
HttpException→{ message, error?, statusCode }(shakli argumentga bog'liq), boshqasi → 500; NestJS 12 —errorCode. - Eng yaqin filtr ushlaydi: metod → controller → global → Nest sukut; xato bitta filtrga tushadi.
BaseExceptionFilter— sukut xulqni saqlab kengaytirish;HttpAdapterHost— platformadan mustaqil javob.- ORM xatolari — ombor qatlamida domen xatosiga; filtr faqat HTTP shaklini biladi.
vazifalar-api:ProblemDetailsFilter—APP_FILTER,LOGDI'dan.
Keyingi dars: Custom dekoratorlar — createParamDecorator bilan @IfMatchShart() va @ToplamYoli(), applyDecorators bilan @VazifaYozuvi() — controller'lardan @Req() ni deyarli butunlay olib tashlaymiz.
Manbalar
- NestJS hujjati — Exception filters (
HttpException,@Catch,ArgumentsHost,BaseExceptionFilter): https://docs.nestjs.com/exception-filters - RFC 9457 — Problem Details for HTTP APIs: https://www.rfc-editor.org/rfc/rfc9457
- OWASP — Error Handling Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html
- Kanon (IlmHamroh, 2026-10-27): lifecycle tajribasi (NestJS 12.2.0, Vitest ichida),
ProblemDetailsFilterningAPP_FILTERga ko'chishi, Prisma ombori xato xaritasi. Demo (HttpExceptionoilasi,errorCode,@Catch(Klass),BaseExceptionFilter) — 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!