Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 1.1 Express'da qanday edi
- 2. O'rnatilgan pipe'lar
- 2.1 Ulash
- 2.2 Har biri nimani qabul qiladi
- 2.3 Qolganlari
- 2.4 DefaultValuePipe
- 3. Pipe sozlamalari
- 4. O'z pipe'ingiz
- 4.1 PipeTransform
- 4.2 Model: pipe zanjiri
- 4.3 DI bilan pipe: "ID → vazifa"
- 5. TypeScript pipe natijasini tekshirmaydi
- 6. Pipe'lar tartibi
- 6.1 Pipe bitta parametrni ko'radi
- 7. Hujumchi nigohi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): pipe'ni tanlang
- 2-mashq (o'rta): ParseIntPipe ning modeli
- 3-mashq (qiyin): qat'iy ID pipe
- 10. Loyiha qadami: SxemaPipe
- 10.1 Nima qilamiz
- 10.2 src/umumiy/sxema.pipe.ts
- 10.3 Tekshiruv
- 11. Real ishda
- Xulosa
- Manbalar
NestJS pipe'lari: ParseIntPipe, DefaultValuePipe va o'z PipeTransform
Qisqacha: Pipe — handler parametrini u chaqirilishidan oldin aylantiradigan yoki rad etadigan klass. Ulanishi:
@Param("id", ParseIntPipe) id: number—"7"satri7soniga aylanadi,"abc"400 oladi. Nest'da tayyorlari bor:ParseIntPipe,ParseFloatPipe,ParseBoolPipe,ParseUUIDPipe,ParseEnumPipe,ParseArrayPipe,ParseDatePipe,DefaultValuePipe. O'zingiznikini yozish —PipeTransformningtransform(qiymat, metadata)metodi. Tuzoqlar:ParseIntPipe"05"ni qabul qiladi va katta sonlarda aniqlikni yo'qotadi,ParseDatePipe2026-02-30ni jim 2-martga aylantiradi.
Bu darsda
- O'rnatilgan pipe'larni ulaysiz va har biri qaysi qiymatni qabul qilib, qaysisini rad etishini haqiqiy chiqishda ko'rasiz.
- Pipe sozlamalarini (
errorHttpStatusCode,optional) va ketma-ket pipe'larni ishlatasiz. PipeTransformbilan o'z pipe'ingizni, DI bilan, yozasiz vaArgumentMetadatani o'qiysiz.- Pipe'lar tartibini va TypeScript parametr turi pipe natijasini tekshirmasligini bilasiz.
vazifalar-apiuchun Standard Schema pipe'ini (SxemaPipe) yozasiz.
Oldin bilishingiz kerak: ValidationPipe sozlamalari va tuzoqlari, Request lifecycle, Provider'lar va service'lar, Number turi JS'da: MAX_SAFE_INTEGER.
ValidationPipe — pipe'larning bittasi, xolos. Bu darsda pipe g'oyasining o'zini ko'ramiz va vazifalar-api ga birinchi o'z pipe'imizni qo'shamiz.
1. Nega bu kerak?
Controller'lar darsida qoida bor edi: @Param va @Query doim satr beradi. GET /vazifalar/7 da id — "7". Handler ichida har safar Number(id) va tekshiruv yozish — takror va unutish oson. O'tgan darsda transform: true ham "yechim" bo'lib ko'rindi, lekin u faqat Number() chaqiradi: "abc" NaN bo'lib o'tadi.
Pipe — shu ishni bir joyga yig'adi. Request lifecycle dagi tartibni eslang: guard'lar va interceptor'lardan keyin, handler'dan oldin pipe'lar ishlaydi. Har parametr o'z pipe'idan o'tadi. Pipe ikki ishdan birini qiladi:
- aylantiradi:
"7"→7,undefined→20(natija keyingi pipe'ga yoki handler'ga ketadi); - rad etadi:
"abc"→ xato tashlaydi, handler chaqirilmaydi va javobni xato filtri yozadi.
O'xshatish: zavod konveyeridagi stansiyalar. Har stansiya detalni yo tayyorlab keyingisiga uzatadi (bo'yaydi, kesadi), yo yaroqsiz deb qutiga tashlaydi. Yig'uvchi (handler) faqat barcha stansiyalardan o'tgan detalni oladi.
1.1 Express'da qanday edi
25-qismda har handler o'z kirishini o'zi tekshirardi: const { id } = tekshir(IdSxemasi, req.params, "params"). Tekshiruv, aylantirish ("7" → 7) va xato (400) — hammasi bitta qatorda, handler'ning boshida. Bu ishonchli edi, lekin uch kamchilik bor edi:
| Express (handler ichida) | Nest pipe | |
|---|---|---|
| Qayerda yoziladi | har handler boshida | parametr dekoratorida yoki global |
| Unutilsa | handler tekshirilmagan qiymat bilan ishlaydi | global pipe hamma parametrni ko'radi |
| Handler nima oladi | req va o'zi ajratadi |
tayyor, tekshirilgan qiymat |
| Narx | yo'q | tartib qoidalarini bilish kerak (pastda) |
Asosiy yutuq — handler toza bo'ladi: unda faqat biznes mantiq qoladi. Narx — tekshiruv endi ko'zdan uzoqda, qaysi pipe qaysi tartibda ishlashini bilish kerak. Bu darsning ikkinchi yarmi aynan shu tartib haqida.
2. O'rnatilgan pipe'lar
2.1 Ulash
Pipe parametr dekoratorining ikkinchi argumenti sifatida beriladi:
@Get(':id')
ol(@Param('id', ParseIntPipe) id: number) { … }
@Get()
royxat(
@Query('limit', new DefaultValuePipe(20), ParseIntPipe)
limit: number,
) { … }Ikki shakl bor: klass (ParseIntPipe) — Nest nusxani o'zi yaratadi (DI bilan), va nusxa (new DefaultValuePipe(20)) — sozlama berish kerak bo'lganda. Bir nechta pipe — vergul bilan, chapdan o'ngga bajariladi.
2.2 Har biri nimani qabul qiladi
Kanon tajribasi har pipe'ga chegaraviy qiymatlar bergan (kurs tajribasi, NestJS 12.2.0, Vitest ichida). Avval eng ko'p ishlatiladigani:
ParseIntPipe
"5" → 5 (number)
"05" → 5 (number)
"-5" → -5 (number)
" 5" → XATO 400 "Validation failed (numeric string is expected)"
"5.0" → XATO 400 "Validation failed (numeric string is expected)"
"1e3" → XATO 400 "Validation failed (numeric string is expected)"
"12abc" → XATO 400 "Validation failed (numeric string is expected)"
"" → XATO 400 "Validation failed (numeric string is expected)"
"9007199254740993" → 9007199254740992 (number)Xato xabari: "Tekshiruv o'tmadi (raqamli satr kutilgan)". ParseIntPipe o'tgan darsdagi transform: true dan ancha qat'iy: "1e3", "5.0", " 5", "" — rad. Lekin ikki joyga qarang:
"05"→ 5 va"-5"→ -5 — o'tadi. ID uchun-5ham,05ham ma'nosiz."9007199254740993"→9007199254740992— boshqa son! Xato yo'q.
Oxirgisini brauzerda tekshiring:
console.log(Number("9007199254740993"));
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.isSafeInteger(9007199254740993));
console.log(BigInt("9007199254740993"));Konsolda:
9007199254740992
9007199254740991
false
9007199254740993nNumber turi darsidagi chegara: Number.MAX_SAFE_INTEGER dan katta butun sonlarni JavaScript aniq saqlay olmaydi — eng yaqin ifodalanadigan songa yaxlitlaydi. ParseIntPipe buni tekshirmaydi. Agar ID'lar 64-bitli bo'lsa (Postgres bigint), boshqa yozuvni o'qish xavfi bor — «Hujumchi nigohi» da qaytamiz. To'liq aniqlik kerak bo'lsa — BigInt (oxirgi qator).
2.3 Qolganlari
Quyidagi chiqishda har pipe uchun bir nechta qiymat bor. Har blokda qaysi qiymat o'tib, qaysisi rad etilganiga qarang — ayniqsa oxirgi ParseDatePipe ga:
ParseFloatPipe
"1.5" → 1.5 (number)
"1e3" → 1000 (number)
"Infinity" → XATO 400 "Validation failed (numeric string is expected)"
ParseBoolPipe
"true" → true (boolean)
"false" → false (boolean)
"TRUE" → XATO 400 "Validation failed (boolean string is expected)"
"1" → XATO 400 "Validation failed (boolean string is expected)"
ParseUUIDPipe (v4)
"2f1c3d8e-6b4a-4c1e-9f2a-1a2b3c4d5e6f" → "2f1c3d8e-6b4a-4c1e-9f2a-1a2b3c4d5e6f" (string)
"2f1c3d8e-6b4a-1c1e-9f2a-1a2b3c4d5e6f" → XATO 400 "Validation failed (uuid v 4 is expected)"
ParseEnumPipe
"yangi" → "yangi" (string)
"Yangi" → XATO 400 "Validation failed (enum string is expected)"
ParseArrayPipe (items: Number, separator ,)
"1,2,3" → [1,2,3] (object)
"1,a" → XATO 400 "[1] item must be a number"
ParseDatePipe
"2026-10-27" → Date(2026-10-27T00:00:00.000Z) (object)
"2026-02-30" → Date(2026-03-02T00:00:00.000Z) (object)
"27.10.2026" → XATO 400 "Validation failed (invalid date format)"(Chiqishdan bir nechta qatorni tushirib qoldirdik.) Har biri haqida bir gap:
ParseFloatPipe—"1e3"ni qabul qiladi (kasr son uchun to'g'ri),"Infinity"ni — yo'q.ParseBoolPipe— faqat aniq"true"/"false". O'tgan darsdagiBoolean("false")tuzog'i yo'q. Lekin"TRUE"va"1"ham rad.ParseUUIDPipe({ version: "4" })— ikkinchi UUID'da versiya raqami (uchinchi guruhning birinchi belgisi)1— v4 emas.ParseEnumPipe(Holat)— registrga sezgir:"Yangi"— rad.ParseArrayPipe({ items: Number, separator: "," })—?id=1,2,3→[1, 2, 3]; xato xabari element indeksini aytadi.ParseDatePipe—"2026-02-30"→ 2-mart. Fevralda 30-kun yo'q, lekin xato o'rniga sana "to'g'rilandi".
Oxirgi tuzoqni ham brauzerda ko'raylik:
const d = new Date("2026-02-30T00:00:00Z");
console.log(d.toISOString());
const haqiqiy = (s) => {
const t = Date.parse(`${s}T00:00:00Z`);
return !Number.isNaN(t) && new Date(t).toISOString().startsWith(s);
};
console.log(haqiqiy("2026-02-30"), haqiqiy("2026-02-28"));Konsolda:
2026-03-02T00:00:00.000Z
false trueDate noto'g'ri kunni keyingi oyga "o'tkazib yuboradi". haqiqiy() — 25-qismdagi vazifalar-api sxemasidagi o'sha tekshiruv: sanani o'qib, qayta satrga aylantiradi va asl bilan solishtiradi.
2.4 DefaultValuePipe
DefaultValuePipe(20)
undefined → 20 (number)
null → 20 (number)
"" → "" (string)
"0" → "0" (string)
DefaultValuePipe(20) → ParseIntPipe (ketma-ket — @Query("limit", new DefaultValuePipe(20), ParseIntPipe))
undefined → 20 (number)
"5" → 5 (number)Sukut qiymat faqat undefined va null uchun (NaN uchun ham). ?limit= (bo'sh satr) — sukut emas: "" o'zgarishsiz qoladi va keyingi ParseIntPipe uni rad etadi. Bu yaxshi: "parametr berilmagan" va "bo'sh berilgan" — turli holatlar.
Tekshirib ko'ring:
@Query("sahifa", new DefaultValuePipe(1), ParseIntPipe) sahifa: number.?sahifa=05,?sahifa=va?(parametrsiz) uchun natija qanday?
Javob
05 → 5 (ParseIntPipe boshidagi nolni qabul qiladi). ?sahifa= → 400: DefaultValuePipe bo'sh satrga tegmaydi, ParseIntPipe "" ni rad etadi. Parametrsiz → 1: undefined sukutga almashdi, keyin ParseIntPipe uni o'tkazdi (allaqachon son).
3. Pipe sozlamalari
O'rnatilgan pipe'larning ko'pi nusxa sifatida sozlanadi. Demo loyihada sinadik (biz ishga tushirdik: NestJS 12.2.0, Node 24.21.0):
@Get('kod/:id')
kod(@Param('id', new ParseIntPipe({
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
})) id: number) { return { id }; }
@Get('ixtiyoriy')
ixtiyoriy(
@Query('limit', new ParseIntPipe({ optional: true }))
limit?: number,
) { return { limit: limit ?? "yo'q" }; }GET /kod/abc → 422 {"message":"Validation failed (numeric string is expected)","error":"Unprocessable Entity","statusCode":422}
GET /ixtiyoriy → 200 {"limit":"yo'q"}
GET /ixtiyoriy?limit=x → 400 {"message":"Validation failed (numeric string is expected)","error":"Bad Request","statusCode":400}errorHttpStatusCode— xato statusini almashtiradi (sukut 400).optional: true— qiymat yo'q bo'lsa xato emas,undefinedo'tadi. Berilgan, lekin noto'g'ri qiymat baribir rad.exceptionFactory—ValidationPipedagi kabi o'z xatongiz.
4. O'z pipe'ingiz
4.1 PipeTransform
Pipe — transform(qiymat, metadata) metodi bor klass. U qaytargan narsa — keyingi pipe'ga (yoki handler'ga) boradi. Xato tashlasa — handler chaqirilmaydi.
export interface PipeTransform<T = any, R = any> {
transform(value: T, metadata: ArgumentMetadata): R;
}Ikkinchi argument — ArgumentMetadata: pipe "qaysi parametr bilan ishlayapman" degan savolga javob oladi.
| Maydon | Nima | Misol |
|---|---|---|
type |
qayerdan | "body", "query", "param", "custom" |
data |
dekorator argumenti | @Param("id") → "id" |
metatype |
parametrning TS turi (klass) | Number, YangiDto |
schema |
Standard Schema obyekti (NestJS 12) | @Body({ schema }) dan |
ValidationPipe metatype dan DTO klassini oladi. schema — NestJS 12 da qo'shilgan maydon; u qayerdan kelishini keyingi darsda ko'ramiz.
4.2 Model: pipe zanjiri
Nest pipe'larni qanday ketma-ket chaqirishini kichik JS modelida ko'raylik:
class TekshiruvXatosi extends Error {}
const sukut = (qiymat) => ({
transform: (v) => (v === undefined || v === null ? qiymat : v),
});
const butun = {
transform(v, m) {
if (typeof v === "number") return v;
if (!/^-?\d+$/.test(v)) {
throw new TekshiruvXatosi(`${m.data}: butun son kutilgan`);
}
return Number(v);
},
};
function parametr(qiymat, metadata, pipelar) {
return pipelar.reduce((v, p) => p.transform(v, metadata), qiymat);
}
const m = { type: "query", data: "limit" };
for (const q of [undefined, "5", "abc"]) {
try {
console.log(q, "→", parametr(q, m, [sukut(20), butun]));
} catch (x) {
console.log(q, "→ 400:", x.message);
}
}Konsolda:
undefined → 20
5 → 5
abc → 400: limit: butun son kutilganreduce (Massivni bitta qiymatga yig'ish) — har pipe oldingisining natijasini oladi. Xato tashlansa, zanjir to'xtaydi — Nest ham xatoni filtrga yuboradi va handler'ni chaqirmaydi. Model soddalashtirilgan: haqiqiy Nest transform dan qaytgan Promise ni ham kutadi (pipe async bo'lishi mumkin) va xatoni Problem Details'ga aylantirish filtrning ishi.
4.3 DI bilan pipe: "ID → vazifa"
Pipe @Injectable() bo'lsa va klass sifatida ulansa, Nest unga bog'liqliklarni beradi. Bu bilan "ID'ni olib, yozuvni topib beradigan" pipe yoziladi:
@Injectable()
class VazifaniTopPipe implements PipeTransform<number> {
constructor(private readonly xizmat: VazifalarXizmati) {}
transform(id: number, m: ArgumentMetadata) {
console.log(` [pipe] qiymat=${String(id)} (${typeof id}), ` +
`type=${m.type}, data=${String(m.data)}`);
const v = this.xizmat.top(id);
if (v === undefined) {
throw new NotFoundException(`Vazifa ${String(id)} yo'q`);
}
return v;
}
}
@Get('vazifa/:id')
vazifa(@Param('id', ParseIntPipe, VazifaniTopPipe) v: Vazifa) {
return v;
}VazifalarXizmati modulning providers ida. Demo natijasi:
[pipe] qiymat=7 (number), type=param, data=id
GET /vazifa/7 → 200 {"id":7,"matn":"Non olish"}
[pipe] qiymat=8 (number), type=param, data=id
GET /vazifa/8 → 404 {"message":"Vazifa 8 yo'q","error":"Not Found","statusCode":404}
GET /vazifa/x → 400 {"message":"Validation failed (numeric string is expected)","error":"Bad Request","statusCode":400}VazifaniTopPipeParseIntPipenatijasini oldi —7(son)./vazifa/xda bizning pipe umuman chaqirilmadi —ParseIntPipezanjirni to'xtatdi.- Handler tayyor vazifani oladi va ichida qidiruv yo'q.
Ya'ni ikki pipe zanjir bo'ldi: birinchisi satrni songa aylantirdi, ikkinchisi sonni yozuvga. Har biri bitta ishni qiladi va har biri alohida sinaladi. Topilmagan ID uchun pipe 404 tashlaydi — handler'gacha yetib bormaydi. Chiqishdagi /vazifa/8 qatori aynan shu: pipe logi bor, handler javobi yo'q, mijoz "Vazifa 8 yo'q" xabarini oldi.
Bu naqsh qulay, lekin ehtiyot bo'ling: pipe ichidagi bazaga murojaat — handler'dan oldin, guard'lardan keyin. Ruxsat tekshiruvi ("bu vazifa shu foydalanuvchinikimi") baribir kerak — u 28-qismdagi IDOR mavzusi.
5. TypeScript pipe natijasini tekshirmaydi
Demo controller'ida ataylab xato tur yozdik:
// pipe number qaytaradi, parametr esa string — tsc xato bermaydi
@Get('tur/:id')
tur(@Param('id', ParseIntPipe) id: string) {
return { id, turi: typeof id };
}tsc hech qanday xato bermadi va ilova ishga tushdi:
GET /tur/5 → 200 {"id":5,"turi":"number"}E'lon qilingan tur — string, haqiqiy qiymat — number. Sabab: parametr dekoratori (ParameterDecorator) TypeScript'ga pipe nima qaytarishini aytmaydi; @Param("id", ParseIntPipe) turi parametr turiga bog'lanmagan. Biz NestJS 12.2.0 da tekshirdik — bu holat o'zgarmagan. Natija: handler ichida id.toUpperCase() yozsangiz, TypeScript ruxsat beradi, ish vaqtida esa TypeError.
Qoida: parametr turini pipe zanjirining oxirgi pipe'i qaytaradigan tur bilan bir xil yozing. Kod review'da @Param(..., ParseIntPipe) x: string — darhol xato.
6. Pipe'lar tartibi
Bitta parametrga bir necha joydan pipe ulanishi mumkin. Kanon tajribasida beshta darajaning hammasi yoqilgan va har pipe o'z nomini yozgan (kurs tajribasi, Vitest ichida):
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)(Raqamlar — so'rovdagi umumiy tartib; oldinroq middleware, guard va interceptor'lar ishlagan.) Tartib: global → controller → metod → parametr. Parametrga yozilgan pipe — eng oxirgi.
flowchart LR
Q["'01' (satr)"] --> G["global pipe"]
G --> C["controller pipe"]
C --> M["metod pipe"]
M --> P["parametr pipe"]
P --> H["handler(id)"]
G -. "xato" .-> F["filtr: 400"]
P -. "xato" .-> FHar o'q — bitta transform() chaqiruvi; har bo'g'in xato tashlasa, so'rov filtrga ketadi va qolgan pipe'lar ham, handler ham chaqirilmaydi. Muhimi: keyingi pipe asl satrni emas, oldingisining natijasini oladi.
Bu tartib o'tgan darsdagi farqni tushuntiradi. class-validator variantida global ValidationPipe({ transform: true }) bor, id uchun esa o'z IdPipe imiz parametrga ulangan:
// id.pipe.ts (tajriba varianti): id faqat ^[1-9]\d{0,9}$
@Injectable()
export class IdPipe implements PipeTransform<string, number> {
transform(qiymat: string): number {
if (!/^[1-9]\d{0,9}$/.test(qiymat)) {
throw new ParametrXatosi("id — musbat butun son", [
{ joy: "params.id", xabar: "id — musbat butun son" },
]);
}
return Number(qiymat);
}
}IdPipe "01" ni rad etishi kerak edi. Lekin global pipe undan oldin ishladi va id: number ni ko'rib Number("01") = 1 qildi. IdPipe satr emas, 1 soni oldi; /^[1-9]\d{0,9}$/.test(1) — 1 ni "1" satriga aylantirib tekshiradi va o'tkazadi. Natija — /v1/vazifalar/01 200 qaytardi.
Zod variantida bu muammo yo'q: IdSxemasi satrni o'zi tekshiradi va o'zi aylantiradi — bitta qadamda, bitta joyda. Tekshiruv va aylantirish turli pipe'larga bo'linsa — ular orasida tartib xatosi uchun joy paydo bo'ladi.
Tekshirib ko'ring: Global pipe
trimqiladi (satrlarning bo'sh joylarini kesadi), parametrda esa@Query("teg", TegPipe)bor — u^#\w+$naqshini tekshiradi.?teg=%20%23uy(ya'ni" #uy") keldi.TegPipenimani ko'radi va natija qanday?
Javob
"#uy" ni — global pipe birinchi ishladi va bo'sh joyni kesdi. Naqsh o'tadi, so'rov qabul qilinadi. Tartib teskari bo'lganda (TegPipe avval) — 400 bo'lardi. Ikkalasi ham "to'g'ri" bo'lishi mumkin; muhimi — natija tartibga bog'liqligini bilish va testda ko'rsatish.
6.1 Pipe bitta parametrni ko'radi
Pipe'ning chegarasi bor: u bitta parametr bilan ishlaydi. "offset va cursor birga berilmasin" kabi qoida ikki parametrga tegishli. @Query("offset", P1) va @Query("cursor", P2) — har pipe faqat o'z qiymatini ko'radi va qoidani tekshira olmaydi.
Yechim — butun obyektni bitta parametr qilib olish: @Query() query va unga bitta sxema yoki pipe. 25-qismdagi RoyxatQuerySxemasi aynan shunday: 11 ta query parametri bitta Zod obyektida va .refine() bilan o'zaro qoidalar. Keyingi darsda bu sxema @Query({ schema }) orqali ulanadi.
7. Hujumchi nigohi
- Aniqlik yo'qolishi.
ParseIntPipe+bigintID'lar:GET /hisoblar/9007199254740993→ handler9007199254740992oladi — boshqa yozuv. Egalik tekshiruvi bo'lmasa, bu begona ma'lumotni o'qish. Himoya — ID uzunligini cheklash (IdPipedagi\d{0,9}), katta ID'lar uchunBigIntyoki satr. - Sana "to'g'rilash".
?gacha=2026-02-30— 2-mart; hisobot yoki jarima muddati jim siljiydi. Himoya — "qayta satrga aylantirib solishtirish" tekshiruvi. - Manfiy va nol ID.
ParseIntPipe-1va0ni o'tkazadi. Ba'zi omborlarda-1"hammasi" yoki "oxirgisi" ma'nosida ishlatilgan bo'ladi. Himoya — musbatlik tekshiruvi. - Pipe ichida bazaga murojaat.
VazifaniTopPipehar so'rovda bazaga boradi — hatto ruxsatsiz foydalanuvchi uchun ham, agar guard yo'q bo'lsa. 404 va 403 farqi ma'lumot oshkor qiladi ("bu ID mavjud"). Himoya — ruxsat tekshiruvi pipe'dan oldin (guard) yoki xizmatda.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
id handler'da satr |
pipe ulanmagan | @Param("id", ParseIntPipe) |
"05", "-5" o'tadi |
ParseIntPipe faqat formatni tekshiradi |
o'z pipe yoki sxema |
| Boshqa yozuv qaytdi (katta ID) | MAX_SAFE_INTEGER dan katta |
uzunlik chegarasi, BigInt |
2026-02-30 qabul qilindi |
ParseDatePipe / new Date |
qayta satrga aylantirib solishtirish |
?limit= 400 beradi |
DefaultValuePipe "" ga tegmaydi |
mijoz parametrni yubormasin yoki o'z pipe |
Ish vaqtida TypeError, tsc jim |
parametr turi pipe natijasiga mos emas | turni oxirgi pipe natijasi bilan yozing |
| O'z pipe'ga son keladi | global transform: true oldin ishladi |
tartibni hisobga oling |
9. Mashqlar
Mashqlar kurs/mashqlar/30/18-pipelar/ da.
1-mashq (oson): pipe'ni tanlang
?faol=true→true—/foydalanuvchi/2f1c…— UUID tekshiruvi —?id=1,2,3→[1, 2, 3]—limitberilmasa 20 —new (20)- O'z pipe'ingiz bajaradigan interfeys —
Yechim
- ParseBoolPipe — faqat
"true"/"false";"1"va"TRUE"rad. - ParseUUIDPipe —
{ version: "4" }bilan versiya ham tekshiriladi. - ParseArrayPipe —
{ items: Number, separator: "," }. - DefaultValuePipe —
undefinedvanulluchun;""uchun emas. - PipeTransform —
transform(qiymat, metadata).
2-mashq (o'rta): ParseIntPipe ning modeli
parseInt pipe xulqini takrorlaydigan parseIntPipe(v) funksiyasini yozing: ^-?\d+$ ga mos kelsa — Number(v), aks holda "Validation failed (numeric string is expected)" xabari bilan xato. Keyin darsdagi chegaraviy qiymatlarni berib, natijani kanon chiqishi bilan solishtiring. Ishora: try/catch (try / catch / finally).
Yechim
function parseIntPipe(v) {
if (typeof v !== "string" || !/^-?\d+$/.test(v)) {
throw new Error("Validation failed (numeric string is expected)");
}
return Number(v);
}
const qiymatlar = [
"5", "05", "-5", " 5", "5.0", "1e3", "", "9007199254740993",
];
for (const v of qiymatlar) {
try {
console.log(JSON.stringify(v), "→", parseIntPipe(v));
} catch (x) {
console.log(JSON.stringify(v), "→ XATO", x.message);
}
}Konsolda:
"5" → 5
"05" → 5
"-5" → -5
" 5" → XATO Validation failed (numeric string is expected)
"5.0" → XATO Validation failed (numeric string is expected)
"1e3" → XATO Validation failed (numeric string is expected)
"" → XATO Validation failed (numeric string is expected)
"9007199254740993" → 9007199254740992Har qator kanon chiqishidagi ParseIntPipe bilan bir xil. Ya'ni pipe aslida "faqat raqamlar, ixtiyoriy minus" degan naqsh va Number() dan iborat — na musbatlik, na boshidagi nol, na MAX_SAFE_INTEGER tekshiriladi. Nest manbasida (@nestjs/common/pipes/parse-int.pipe.js, 12.2.0) ham deyarli shu: /^-?\d+$/ naqsh, isFinite va parseInt(String(value), 10).
3-mashq (qiyin): qat'iy ID pipe
idPipe(v) ni yozing: faqat ^[1-9]\d{0,9}$ (boshida nol yo'q, ko'pi bilan 10 raqam) va natija Number.isSafeInteger bo'lsin. Xato — { status: 400, joy: "params.id", xabar: "id — musbat butun son" } obyekti bilan. Bundan tashqari, global transform: true dan son kelib qolsa ham to'g'ri ishlasin: son kelsa — String(v) qilib, xuddi shu qoida bilan tekshirsin. Ishora: typeof, Number.isSafeInteger (Number turi).
Yechim
function idPipe(v) {
const satr = typeof v === "number" ? String(v) : v;
if (typeof satr !== "string" || !/^[1-9]\d{0,9}$/.test(satr)) {
throw {
status: 400, joy: "params.id", xabar: "id — musbat butun son",
};
}
const n = Number(satr);
if (!Number.isSafeInteger(n)) {
throw { status: 400, joy: "params.id", xabar: "id juda katta" };
}
return n;
}
const sinov = ["7", "01", "-1", "1e3", "9007199254740993", 7, NaN];
for (const v of sinov) {
try {
console.log(String(v), "→", idPipe(v));
} catch (x) {
console.log(String(v), "→", x.status, x.xabar);
}
}Konsolda:
7 → 7
01 → 400 id — musbat butun son
-1 → 400 id — musbat butun son
1e3 → 400 id — musbat butun son
9007199254740993 → 400 id — musbat butun son
7 → 7
NaN → 400 id — musbat butun sonKatta son \d{0,9} (ko'pi bilan 10 raqam) bilan oldinroq ushlandi, shuning uchun isSafeInteger tekshiruvi bu ro'yxatda ishga tushmadi — u uzunlik chegarasi o'zgarsa himoya bo'lib qoladi. NaN kelsa (transform: true + "abc"), String(NaN) = "NaN" va naqsh uni rad etadi. Lekin "01" dan kelgan 1 soni farqlanmaydi — global pipe ma'lumotni allaqachon yo'qotgan. Shuning uchun tekshiruv aylantirishdan oldin bo'lishi kerak — keyingi darsdagi Standard Schema pipe'i aynan shunday.
10. Loyiha qadami: SxemaPipe
10.1 Nima qilamiz
vazifalar-api da o'rnatilgan pipe'lar ishlatilmaydi — ParseIntPipe ning "01" va katta son xulqi 25-qismdagi IdSxemasi dan bo'shroq. Kirishni 25-qismdagi Zod sxemalari tekshirishda davom etadi. Lekin ular endi handler ichida emas, pipe ichida chaqiriladi.
Bu darsda pipe faylining o'zini yozamiz. Uni controller'larga ulash va ishga tushirish — keyingi darsda.
Validatsiya darslarida tajriba/class-validator tarmog'iga o'tgan bo'lsangiz, avval asosiy ish tarmog'iga qayting: git switch nest. git status birinchi qatorda On branch nest deb yozishi kerak. Bu darsdan boshlab o'zgarishlar yana nest tarmog'ida.
10.2 src/umumiy/sxema.pipe.ts
Pipe parametr metama'lumotidagi schema maydonini o'qiydi. Bu maydonni NestJS 12 ning @Body({ schema }), @Query({ schema }), @Param({ schema }) yozuvi to'ldiradi — hozircha shuni bilish yetarli, batafsil keyingi darsda. schema — Standard Schema obyekti: Zod, Valibot yoki ArkType sxemasi bo'lishi mumkin (Backend validatorlari darsidagi ~standard.validate).
Kanon faylida qatorlar uzun edi — telefon uchun bo'ldik va izohlarni qisqartirdik (kod ma'nosi aynan). Bo'lingan nusxani kanon nusxasida tsc --noEmit, ESLint va Vitest bilan tekshirdik — o'zgarishsiz o'tdi.
// sxema.pipe.ts — global pipe: Nest 12 `@Body({ schema })`,
// `@Query({ schema })`, `@Param({ schema })` parametr
// metama'lumotiga Standard Schema (Zod, Valibot, ArkType…)
// obyektini qo'yadi; shu pipe uni tekshiradi va xatoni Problem
// Details turiga aylantiradi: body → 422 ValidatsiyaXatosi,
// query/param → 400 ParametrXatosi ("joy" — 25-qismdagidek).
// O'rnatilgan StandardSchemaValidationPipe emas: u hamma xatoga
// bitta status beradi va exceptionFactory parametr turini bilmaydi
import { Injectable } from "@nestjs/common";
import type { ArgumentMetadata, PipeTransform } from "@nestjs/common";
import type { StandardSchemaV1 } from "@standard-schema/spec";
import { ParametrXatosi, ValidatsiyaXatosi } from "./xatolar.ts";
// tekshir.ts ichidagi z.config(z.locales.uz()) — o'z xabarimiz
// bo'lmagan qoidalar ham o'zbekcha
import "./tekshir.ts";
import type { MaydonXatosi } from "./xatolar.ts";
const JOY: Partial<Record<ArgumentMetadata["type"], string>> = {
body: "body", query: "query", param: "params",
};
function yolSegmenti(
s: PropertyKey | StandardSchemaV1.PathSegment,
): string {
return String(typeof s === "object" ? s.key : s);
}
@Injectable()
export class SxemaPipe implements PipeTransform {
async transform(
qiymat: unknown,
metadata: ArgumentMetadata,
): Promise<unknown> {
const sxema = metadata.schema;
const joy = JOY[metadata.type];
// sxemasiz parametr (@Req, @Res, sxemasiz @Body) — o'zgarishsiz
if (sxema === undefined || joy === undefined) return qiymat;
// @Param("id", { schema }) — bitta qiymat; joy "params.id"
const ildiz =
metadata.data === undefined ? [joy] : [joy, metadata.data];
const natija = await sxema["~standard"].validate(qiymat);
if (natija.issues === undefined) return natija.value;
const xatolar: MaydonXatosi[] = natija.issues.map((i) => ({
joy: [...ildiz, ...(i.path ?? []).map(yolSegmenti)].join("."),
xabar: i.message,
}));
if (metadata.type === "body") {
throw new ValidatsiyaXatosi(xatolar);
}
const birinchi = xatolar[0]?.xabar ?? "noto'g'ri";
throw new ParametrXatosi(birinchi, xatolar);
}
}Qatorma-qator asosiy joylar:
metadata.schema === undefined—@Req(),@Res()va sxemasiz@Body()parametrlari pipe'dan o'zgarishsiz o'tadi. Global pipe hamma parametrga ulanadi — bu shart majburiy.joy—body,query,params;metadata.databo'lsa (@Param("id", ...)), u ham qo'shiladi. Natija — 25-qismdagijoybilan bir xil:body.vazifalar.1.matn,query.limit.sxema["~standard"].validate(qiymat)— kutubxonaga bog'liq emas.issuesbo'lmasa —natija.value: Zod.transform()larining natijasi ("7"→7,"#UY"→"uy"). Aylantirish va tekshiruv — bitta qadamda.- 422 yoki 400 —
metadata.typebo'yicha. O'tgan darsdaexceptionFactoryaynan shuni bilmagani uchunValidationPipeni o'rab olish kerak bo'lgan edi; o'z pipe'ida bu birif. import "./tekshir.ts"— faqat yon ta'siri uchun: o'sha faylda Zod'ning o'zbekcha xabarlari yoqiladi (z.config(z.locales.uz())).
@standard-schema/spec — faqat tur paketi (dev bog'liqlik). U @nestjs/common ning o'z bog'liqligi sifatida allaqachon o'rnatilgan, lekin to'g'ridan import qilganimiz uchun package.json ga aniq versiya bilan yoziladi:
npm install -D -E @standard-schema/spec@1.1.010.3 Tekshiruv
Pipe hali hech qayerga ulanmagan, shuning uchun API xulqi o'zgarmaydi. npm run tip va npm run lint toza o'tishi kerak — fayl o'zi to'g'ri yozilganini shunday bilamiz.
Hali commit qilmang — pipe keyingi darsda ulanadi, loyiha qadami esa Javobni serializatsiya qilish darsida commit bilan yopiladi.
11. Real ishda
ParseIntPipe,ParseUUIDPipe— Nest loyihalaridagi eng ko'p uchraydigan pipe'lar.findOne(@Param('id', ParseIntPipe) id: number)—nest g resourceshabloni.- "Entity pipe" (ID → yozuv) — ba'zi jamoalarda mashhur, ba'zilarida taqiqlangan: mantiq yashirin joyga ketadi va ruxsat tekshiruvi esdan chiqadi.
- Kod review'da
@Param("id") id: numberni pipe'siz ko'rsangiz — bu xato: turda son, aslida satr. Globaltransform: truebo'lsa —NaNxavfi (ValidationPipe sozlamalari va tuzoqlari). Har ID parametrida aniq pipe yoki sxema bo'lsin. - Intervyu: "pipe va guard farqi?", "pipe'lar qaysi tartibda ishlaydi?", "
ParseIntPipenimani tekshirmaydi?", "ArgumentMetadatada nima bor?".
Xulosa
- Pipe — handler'dan oldin parametrni aylantiradi yoki rad etadi; zanjirda chapdan o'ngga, global → controller → metod → parametr.
- O'rnatilgan pipe'lar qulay, lekin tuzoqli:
ParseIntPipe—"05",-5, katta sonlarda aniqlik;ParseDatePipe—02-30→03-02;DefaultValuePipe—""ga tegmaydi. - O'z pipe —
PipeTransform.transform(qiymat, metadata);ArgumentMetadata:type,data,metatype,schema(NestJS 12). - TypeScript parametr turini pipe natijasi bilan solishtirmaydi — turni o'zingiz to'g'ri yozing.
- Pipe bitta parametrni ko'radi; parametrlar orasidagi qoida uchun butun obyekt (
@Query()) bitta sxema bilan tekshiriladi. vazifalar-apigaSxemaPipeqo'shildi: Standard Schema bilan tekshiradi, body — 422, query/param — 400.
Keyingi dars: Zod va Standard Schema NestJS'da — @Body({ schema }) qanday ishlaydi, SxemaPipe controller'larga ulanadi, nestjs-zod va o'lchov: nega kanon Zod'ni tanladi.
Manbalar
- NestJS hujjati — Pipes (o'rnatilgan pipe'lar, custom pipe,
ArgumentMetadata): https://docs.nestjs.com/pipes - MDN —
Number.MAX_SAFE_INTEGER: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER - Standard Schema spetsifikatsiyasi: https://standardschema.dev
- Kanon (IlmHamroh, 2026-10-27): pipe'lar tajribasi (NestJS 12.2.0, Vitest ichida), lifecycle tartibi, class-validator variantidagi
IdPipe,vazifalar-apiSxemaPipe. Demo (pipe sozlamalari, DI bilan pipe, tur tekshiruvi) — 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!