8.5-bob: DTO, validation pipes
8-QISM — NestJS (chuqur) · 5-mavzu
1. Kirish va motivatsiya
Ma'lumot modeli 8.4-bob tayyor. Endi backend'ning birinchi himoya chizig'iga — kiruvchi ma'lumotni tekshirishga — o'tamiz. 5.9-bobda "Hech qachon foydalanuvchiga ishonmang" (14) tamoyilini va Zod/Joi/class-validator'ni o'rgandik. NestJS'da bu — DTO (Data Transfer Object) + ValidationPipe + class-validator orqali, juda toza va deklarativ tarzda amalga oshiriladi. Bu — har endpoint'ning eshik qorovuli (5.9 o'xshatishi): noto'g'ri ma'lumot biznes logikaga yetib bormaydi.
DTO — "ma'lumot tashish obyekti" — controller qabul qiladigan ma'lumot shaklini belgilovchi class. Masalan, CreateUserDto — "user yaratish uchun ism, email, parol kerak" deydi. class-validator — DTO maydonlariga dekorator (@IsEmail, @MinLength — 7.6) bilan qoidalar qo'shadi. ValidationPipe — kelgan ma'lumotni DTO'ga solib, avtomatik tekshiradi (noto'g'ri bo'lsa — 400). Bu — 5.9'dagi qo'lda validatsiya middleware'ning NestJS'dagi tashkillangan, deklarativ versiyasi.
NestJS'ning kuchli tomoni: ValidationPipe global o'rnatiladi (bir marta — main.ts), keyin har DTO avtomatik tekshiriladi (har endpoint'da qo'lda yozmaysiz). Va mapped types (PartialType, PickType, OmitType — 7.4 utility'larning NestJS versiyasi) — bir DTO'dan boshqasini yasaydi (Create Update). Bu bob: DTO, class-validator, ValidationPipe (whitelist/transform), mapped types, va custom pipe — chuqur. Bu — har NestJS endpoint'ning poydevori.
O'xshatish: DTO + ValidationPipe — anketa va tekshiruvchi. DTO — to'ldirilishi kerak bo'lgan anketa shakli (qaysi maydonlar, qanday). class-validator — har maydon yonidagi qoida ("email formatida", "kamida 8 belgi"). ValidationPipe — tekshiruvchi xodim: anketani oladi, qoidalarga solib tekshiradi, noto'g'ri bo'lsa qaytaradi (400), to'g'ri bo'lsa ichkariga o'tkazadi. Anketasiz/tekshiruvsiz — har xil chala ma'lumot kiradi (xato, xavf — 14).
Nega muhim?
- Birinchi himoya (14) — "foydalanuvchiga ishonmang" 5.9-bob — NestJS'da DTO.
- Avtomatik — global pipe; har endpoint qo'lda emas.
- Type-safe — DTO class — controller'da tur (autocompletion).
- DRY — mapped types (Create Update).
2. Nazariya — chuqur tushuntirish
2.1. DTO nima (Data Transfer Object)
DTO — controller/client orasidagi ma'lumot shaklini belgilovchi class:
// create-user.dto.ts
export class CreateUserDto {
ism: string;
email: string;
parol: string;
}DTO — "qaysi ma'lumot, qanday shaklda" (kirish/chiqish shartnomasi). Entity (8.4 — DB modeli) dan alohida: DTO — API qatlamida (kirish), entity — DB qatlamida. Sababi: DB modeli ≠ API ma'lumoti (masalan DTO'da parol bor, javobda yo'q — 14). DTO — class (interface emas — class-validator dekoratorlari uchun — 2.3).
2.2. Nega DTO entity'dan alohida
Entity 8.4-bob: DTO:
- DB strukturasi - API kirish/chiqish
- id, createdAt (avto) - faqat kerakli maydonlar
- parol (saqlanadi) - CreateDto: parol bor; ResponseDto: parol yo'q (14)
- relations - oddiy (yoki tanlangan)
DTO entity'ni "tashqi dunyo"dan ajratadi (xavfsizlik + moslashuvchanlik)DTO ≠ Entity (best practices): entity'ni to'g'ridan API'da ishlatmang. DTO — API "yuzi": faqat kerakli maydonlar (Create — id'siz; Response — parolsiz — 14). Entity o'zgarsa, API o'zgarmaydi (ajratilgan). Bu — qatlam ajratish (8.1: 2.9, 9).
2.3. class-validator (validatsiya dekoratorlari)
class-validator — DTO maydonlariga dekorator 7.6-bob bilan qoidalar (5.9: class-validator):
npm install class-validator class-transformerimport { IsString, IsEmail, MinLength, IsOptional, IsInt, Min } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(2, { message: "Ism kamida 2 belgi" })
ism: string;
@IsEmail({}, { message: "Email noto'g'ri" })
email: string;
@MinLength(8)
parol: string;
@IsOptional() // ixtiyoriy
@IsInt() @Min(18)
yosh?: number;
}class-validator dekoratorlari 7.6-bob:
@IsString,@IsEmail,@MinLength,@IsInt,@Min/@Max,@IsOptional,@IsEnum,@IsArray,@Matches(RegEx). Har maydonga deklarativ qoida (5.9 — qo'lda schema o'rniga). DTO class bo'lishi shart (dekorator uchun — 2.1).
Eng ko'p ishlatiladigan dekoratorlar (to'liq ma'lumotnoma):
── Tur (type) ──────────────────────────────────────────────
@IsString() — matn
@IsInt() — butun son
@IsNumber() — son (kasr ham)
@IsBoolean() — true/false
@IsDate() — Date obyekti (@Type(() => Date) bilan)
@IsArray() — massiv
@IsObject() — obyekt
@IsEnum(Enum) — enum qiymati (faqat ruxsat etilgan)
── Mavjudlik (presence) ────────────────────────────────────
@IsNotEmpty() — bo'sh bo'lmasin ("" yoki null emas)
@IsOptional() — ixtiyoriy (yo'q bo'lsa — qolgan qoidalar o'tkazib yuboriladi)
@IsDefined() — undefined bo'lmasin
── Format (string) ─────────────────────────────────────────
@IsEmail() — email formati
@IsUUID("4") — UUID (v4 — 8.4: PK)
@IsUrl() — URL
@IsPhoneNumber("UZ") — telefon (mamlakat kodi)
@IsJSON() — JSON matni
@Matches(/regex/) — RegEx moslik (parol murakkabligi — Misol 1)
── Uzunlik/diapazon (length/range) ─────────────────────────
@Length(2, 50) — matn uzunligi [min, max]
@MinLength(8) — matn min uzunlik
@MaxLength(255) — matn max uzunlik
@Min(18) / @Max(120) — son diapazoni
@ArrayMinSize(1) — massiv min elementlar (Misol 4)
@ArrayMaxSize(50) — massiv max elementlar (DoS — 14)
── Qiymat (value) ──────────────────────────────────────────
@IsIn(["a", "b"]) — faqat shu qiymatlardan biri
@IsPositive() — musbat son
@IsCreditCard() — karta raqami (2.12)// @Length / @IsUUID / @IsDate misoli
import { IsString, Length, IsUUID, IsDate } from "class-validator";
import { Type } from "class-transformer";
export class UpdateEventDto {
@IsUUID("4", { message: "ID — UUID (v4) bo'lishi kerak" })
id: string; // 8.4: PK (UUID)
@IsString()
@Length(3, 100, { message: "Sarlavha 3–100 belgi" }) // min VA max birga
sarlavha: string;
@IsDate({ message: "Sana noto'g'ri" })
@Type(() => Date) // transform: string (ISO) Date (2.6)
boshlanish: Date;
}
@Length(min, max)— matn uzunligini bir dekoratorda cheklaydi (@MinLength+@MaxLengtho'rniga).@IsUUID("4")— UUID PK 8.4-bob tekshiruvi — noto'g'ri format 400 qaytaradi (DB'ga so'rov ketmaydi).@IsDate—@Type(() => Date)bilan birga (transform ISO matnniDateobyektiga aylantiradi, keyin@IsDatetekshiradi). Dekoratorlar birga qo'llanadi (tur + uzunlik + format), har biri alohida xato xabari.
2.4. ValidationPipe (avtomatik tekshiruv)
ValidationPipe — DTO'ni avtomatik tekshiradi:
// main.ts — global (bir marta — 8.1: 2.10)
app.useGlobalPipes(new ValidationPipe());
// Controller — DTO ishlatish (avtomatik tekshiriladi)
@Post()
yarat(@Body() dto: CreateUserDto) { // ValidationPipe dto'ni tekshiradi
// bu yerga FAQAT to'g'ri ma'lumot keladi (noto'g'ri 400 avtomatik)
return this.usersService.yarat(dto);
}ValidationPipe — kelgan
@Body()ni DTO class'iga transform qiladi (class-transformer), keyin class-validator bilan tekshiradi. Noto'g'ri 400 (avtomatik, xato xabarlari bilan — 5.7). Global (main.ts) — har DTO avtomatik. Bu — 5.9 middleware'ning NestJS versiyasi (deklarativ, avtomatik).
2.5. ValidationPipe opsiyalari (muhim — xavfsizlik)
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // DTO'da YO'Q maydonlarni O'CHIRADI (mass assignment — 14)
forbidNonWhitelisted: true, // yoki: ortiqcha maydon 400 (xato)
transform: true, // ma'lumotni DTO tur'iga AYLANTIRADI (string number)
transformOptions: { enableImplicitConversion: true },
disableErrorMessages: false, // productionda true — xato tafsilotlarini yashiradi (14)
}));Muhim opsiyalar (best practices):
whitelist: true— DTO'da yo'q maydonlarni o'chiradi (mass assignment himoyasi —{ rol: "admin" }o'tmaydi — 5.9: 2.13, 14).forbidNonWhitelisted: true— ortiqcha maydon xato (qattiqroq).transform: true— query/param string'ni DTO turi'ga (number) aylantiradi. Bu uchtasi — majburiy xavfsizlik sozlamasi.
disableErrorMessages—truebo'lsa, javobda batafsil validatsiya xabarlari (messagemassivi — Misol 10) berilmaydi, faqatBad Requestqoladi. Foydasi: production'da DTO ichki tuzilishini oshkor qilmaslik (ba'zi jamoalar buni afzal ko'radi). Kamchiligi: frontend 11.10-bob aniq xabarni ko'rsata olmaydi. Odatdafalse(xabarlar kerak) — lekin maxfiy API'daprocess.env.NODE_ENV === "production"bo'lsatrueqilinadi. Yana ikki opsiya:stopAtFirstError: true(birinchi xatoda to'xtaydi — barcha xatolar emas),skipMissingProperties: true(yo'q maydonlarni tekshirmaydi — PATCH uchun, lekin@IsOptionalaniqroq).
2.6. Transform (tur aylantirish)
// transform: true — query string DTO turi
export class FilterDto {
@IsInt() @Min(1)
@Type(() => Number) // class-transformer — string number (query)
page: number = 1;
@IsOptional() @IsString()
qidiruv?: string;
}
@Get()
hammasi(@Query() filter: FilterDto) { // page — number (transform!)
return this.service.hammasi(filter);
}
transform: true+@Type— query/param string (5.7 — HTTP'da hammasi string) ni DTO turiga (number) aylantiradi.@Type(() => Number)(class-transformer) — aniq aylantirish.enableImplicitConversion— avtomatik (lekin aniq@Typeishonchliroq). Bu — 5.9: 2.7 (Zod coerce) NestJS versiyasi.
@Transform — maxsus (custom) aylantirish. @Type faqat turni almashtiradi; qiymatni o'zgartirish (trim, lowercase, split) kerak bo'lsa — @Transform:
import { Transform, Type } from "class-transformer";
import { IsString, IsEmail, IsArray } from "class-validator";
export class CreateUserDto {
@IsEmail()
@Transform(({ value }) => value?.toLowerCase().trim()) // email — kichik harf + trim (5.9: normalizatsiya)
email: string;
@IsString()
@Transform(({ value }) => value?.trim()) // bo'shliqlarni olib tashla
ism: string;
@IsArray()
@Transform(({ value }) => // "a,b,c" ["a","b","c"] (query)
typeof value === "string" ? value.split(",") : value,
)
teglar: string[];
}
@Transform(({ value }) => ...)— validatsiyadan oldin qiymatni o'zgartiradi (class-transformer). Foydali: email'ni kichik harfga (test@X.comvaTEST@x.COMbir xil bo'lsin — 5.9), matnni trim, CSV query'ni massivga.({ value, key, obj })— qiymat, maydon nomi, butun obyekt.@Type(tur) +@Transform(qiymat) — birga ishlaydi.
plainToInstance — DTO'ni qo'lda yasash. ValidationPipe buni avtomatik qiladi; lekin pipe'dan tashqarida (masalan servisda, testda, response yasashda) qo'lda kerak bo'lsa:
import { plainToInstance } from "class-transformer";
import { validate } from "class-validator";
// oddiy obyekt DTO class instance (dekoratorlar/@Transform ishlaydi)
const dto = plainToInstance(CreateUserDto, {
email: "TEST@X.COM", ism: " Ali ",
});
// dto.email === "test@x.com", dto.ism === "Ali" (@Transform qo'llandi)
const xatolar = await validate(dto); // qo'lda tekshiruv (ValidationError[])
if (xatolar.length > 0) { /* ... */ }
plainToInstance(Class, obj)(class-transformer — eski nomiplainToClass) — oddiy JSON obyektni DTO instance'ga aylantiradi (@Type,@Transform,@Excludeshu paytda ishlaydi). ValidationPipe ichki mexanizmi shu. Qo'lda kerak bo'ladigan joylar: Response DTO yasash (2.13 —plainToInstance(UserResponseDto, user)), servis ichida tekshiruv (validate(dto)), test'lar. Odatda pipe avtomatik qiladi — bu esa "eshik ortidagi" mexanizm.
2.7. Mapped types (DTO'dan DTO — DRY)
Mapped types — bir DTO'dan boshqasini yasaydi (7.4 utility'larning NestJS versiyasi — docs):
import { PartialType, PickType, OmitType } from "@nestjs/mapped-types"; // yoki @nestjs/swagger
// Create DTO
export class CreateUserDto {
@IsString() ism: string;
@IsEmail() email: string;
@MinLength(8) parol: string;
}
// Update DTO — hamma maydon IXTIYORIY (PartialType — 7.4: Partial)
export class UpdateUserDto extends PartialType(CreateUserDto) {}
// { ism?, email?, parol? } — validatsiya saqlanadi!
// Boshqa: PickType (tanlash), OmitType (chiqarish — 7.4)
export class LoginDto extends PickType(CreateUserDto, ["email", "parol"]) {}
// IntersectionType — ikki DTO'ni BIRLASHTIRISH (7.4: & intersection)
import { IntersectionType } from "@nestjs/mapped-types";
export class PaginationDto {
@IsOptional() @Type(() => Number) @IsInt() @Min(1)
page: number = 1;
}
export class SearchDto {
@IsOptional() @IsString()
qidiruv?: string;
}
// ikkalasining maydonlari (page + qidiruv) — validatsiya bilan
export class UserQueryDto extends IntersectionType(PaginationDto, SearchDto) {}Mapped types (7.4 utility — NestJS):
PartialType(hamma ixtiyoriy — Update),PickType(tanlash — Login),OmitType(chiqarish),IntersectionType(ikki DTO birlashtirish — masalanPagination + Searchbitta query DTO). Validatsiya dekoratorlari saqlanadi (Create'dagi@IsEmailUpdate'da ham ishlaydi). Bu — DRY (Create Update qo'lda takrorlamasdan — 7.4). To'rttasini kombinatsiya qilish mumkin:PartialType(PickType(...)),IntersectionType(A, PartialType(B))va h.k.
2.8. Nested validation (ichma-ich obyekt/massiv)
import { ValidateNested, IsArray } from "class-validator";
import { Type } from "class-transformer";
export class AddressDto {
@IsString() shahar: string;
@IsString() kocha: string;
}
export class CreateUserDto {
@IsString() ism: string;
@ValidateNested() // ichki obyektni tekshir
@Type(() => AddressDto) // transform (class-transformer)
manzil: AddressDto;
@IsArray()
@ValidateNested({ each: true }) // har element (massiv)
@Type(() => OrderItemDto)
items: OrderItemDto[];
}Nested validation (ichma-ich):
@ValidateNested()+@Type(() => Dto)— ichki obyekt/massivni tekshiradi (8.4: Misol 7).{ each: true }— massiv har elementi. Murakkab DTO (buyurtma + items) uchun zarur.@Type— class-transformer ichki turni biladi.
2.9. Pipe nima (umumiy — 8.1: request lifecycle)
Pipe — so'rov ma'lumotini transform yoki validate qiladigan NestJS qatlami:
Pipe ikki vazifa:
1. Transformation — ma'lumotni o'zgartirish (string number, sana parse)
2. Validation — tekshirish (noto'g'ri xato)
Request lifecycle (8.1: 2.12): ... Pipe Controller handler
Pipe handler'dan OLDIN ishlaydi (ma'lumot tayyor/tekshirilgan handler'ga)Pipe — request lifecycle'da (8.1: 2.12) controller'dan oldin. ValidationPipe — validatsiya pipe; ParseIntPipe — transform pipe. Built-in pipe'lar + custom 2.11-bob. Pipe — "ma'lumot tozalovchi" (handler'ga tayyor ma'lumot).
2.10. Built-in pipe'lar (ParseIntPipe, ...)
import { ParseIntPipe, ParseUUIDPipe, ParseBoolPipe, DefaultValuePipe } from "@nestjs/common";
@Get(":id")
bitta(@Param("id", ParseIntPipe) id: number) { // string number (yoki 400)
return this.service.bitta(id); // id — number (transform + validate)
}
@Get()
hammasi(@Query("page", new DefaultValuePipe(1), ParseIntPipe) page: number) {
return this.service.hammasi(page);
}Built-in pipe'lar:
ParseIntPipe(string number, noto'g'ri 400),ParseUUIDPipe(UUID),ParseBoolPipe,DefaultValuePipe(default qiymat),ParseArrayPipe. Bitta parametr uchun (DTO'siz).@Param("id", ParseIntPipe)— id'ni number qiladi va tekshiradi.
Barcha built-in pipe'lar (misollar bilan):
import {
ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseUUIDPipe,
ParseEnumPipe, ParseArrayPipe, DefaultValuePipe,
} from "@nestjs/common";
// ParseBoolPipe — "true"/"false" (yoki "1"/"0") boolean
@Get()
faol(@Query("active", ParseBoolPipe) active: boolean) { // ?active=true true
return this.service.faol(active);
}
// ParseEnumPipe — faqat enum qiymatlari (aks holda 400)
enum Rol { USER = "user", ADMIN = "admin" }
@Get("rol/:rol")
byRol(@Param("rol", new ParseEnumPipe(Rol)) rol: Rol) { // "user"/"admin" — yoki 400
return this.service.byRol(rol);
}
// ParseArrayPipe — CSV yoki massiv tekshirilgan massiv
@Get()
byIds(
@Query("ids", new ParseArrayPipe({ items: Number, separator: "," }))
ids: number[], // ?ids=1,2,3 [1,2,3] (har biri number)
) {
return this.service.byIds(ids);
}
// ParseFloatPipe — kasr son
@Get("narx")
narx(@Query("min", ParseFloatPipe) min: number) { // ?min=9.99 9.99
return this.service.narx(min);
}
// Pipe'ga XATO xabari sozlash (optionsFactory)
@Get(":id")
bitta(
@Param("id", new ParseIntPipe({ errorHttpStatusCode: 422 })) // 422 (400 o'rniga)
id: number,
) {
return this.service.bitta(id);
}To'liq ro'yxat:
ParseIntPipe,ParseFloatPipe(kasr),ParseBoolPipe("true"/"false" boolean),ParseUUIDPipe(UUID versiyasi bilan —new ParseUUIDPipe({ version: "4" })),ParseEnumPipe(Enum)(faqat enum qiymati — yo'l parametri uchun ajoyib),ParseArrayPipe({ items, separator })(CSV tekshirilgan massiv),DefaultValuePipe(qiymat)(yo'q bo'lsa default). Har biri transform + validate (noto'g'ri 400, yokierrorHttpStatusCodebilan boshqa kod). Bular — bitta parametr uchun; ko'p maydonli obyekt uchun — DTO + ValidationPipe.
2.11. Custom pipe (o'z pipe'ing)
import { PipeTransform, Injectable, BadRequestException } from "@nestjs/common";
// Custom validation/transform pipe (2.9)
@Injectable()
export class TrimPipe implements PipeTransform {
transform(value: any) {
if (typeof value === "string") return value.trim(); // tozalash (5.9: sanitatsiya)
return value;
}
}
// Custom: ObjectId validatsiya (Mongo — 6.2)
@Injectable()
export class ParseObjectIdPipe implements PipeTransform {
transform(value: string) {
if (!mongoose.isValidObjectId(value)) {
throw new BadRequestException("Noto'g'ri ID"); // (8.1)
}
return value;
}
}Custom pipe —
PipeTransforminterface (transformmetod). Transform (trim, parse) yoki validate (custom qoida).@Param("id", ParseObjectIdPipe)bilan ishlatiladi. ValidationPipe ko'p holatni qoplaydi; custom — maxsus mantiq (Mongo ObjectId, sana parse).
2.12. Validation guruhlari va shartli
// Bir DTO — turli kontekstda (guruh)
export class UserDto {
@IsString({ groups: ["create", "update"] }) ism: string;
@IsEmail({}, { groups: ["create"] }) email: string; // faqat create'da
}
// Shartli validatsiya (@ValidateIf)
export class PaymentDto {
@IsString() usul: string;
@ValidateIf((o) => o.usul === "karta") // faqat karta bo'lsa
@IsCreditCard()
karta?: string;
}Validation guruhlari (
groups) — bir DTO turli kontekstda (create/update).@ValidateIf— shartli (boshqa maydonga qarab — masalan to'lov usuli). Murakkab DTO uchun. Ko'p holatda alohida DTO (mapped types — 2.7) soddaroq.
2.13. Response DTO (chiqishni nazorat — 14)
import { Exclude, Expose } from "class-transformer";
// Response — chiqishni nazorat (parolsiz — 14)
export class UserResponseDto {
@Expose() id: number;
@Expose() ism: string;
@Expose() email: string;
@Exclude() parol: string; // chiqishda OLIB TASHLANADI (14)
}
// Controller — ClassSerializerInterceptor bilan (8.7)
@UseInterceptors(ClassSerializerInterceptor)
@Get(":id")
bitta(@Param("id") id: number) {
return this.service.bitta(id); // parol avtomatik olib tashlanadi
}Response DTO +
@Exclude— chiqishni nazorat (kirish DTO — kelishni; response — ketishni).@Exclude()(parol) — javobda olib tashlanadi (14, 5.15).ClassSerializerInterceptor8.7-bob — buni avtomatik qiladi. Maxfiy ma'lumot (parol) hech qachon chiqmaydi.
2.14. Validatsiya + Swagger (8.8 ko'prik)
// @nestjs/swagger mapped types — Swagger ham (8.8)
import { PartialType } from "@nestjs/swagger"; // (mapped-types o'rniga — Swagger uchun)
import { ApiProperty } from "@nestjs/swagger";
export class CreateUserDto {
@ApiProperty({ example: "Ali" }) // Swagger hujjat (8.8)
@IsString() ism: string;
}
@nestjs/swaggermapped types (PartialTypeshu paketdan) — DTO ham validatsiya, ham Swagger hujjat 8.8-bob uchun.@ApiProperty— Swagger'da maydon (example). DTO bir vaqtda: tur + validatsiya + hujjat 8.8-bob. Bitta manba (DRY).
2.15. Best practices (DTO/validatsiya — 14)
DTO entity'dan alohida (kirish/chiqish ajratish — 2.2)
Global ValidationPipe (whitelist + transform + forbidNonWhitelisted — 2.5, 14)
class-validator dekoratorlar (har maydon — 2.3)
Mapped types (Create Update — DRY — 2.7)
Nested validation (ichma-ich — 2.8)
Response DTO (@Exclude — parol — 14, 2.13)
Built-in pipe (ParseIntPipe — DTO'siz parametr — 2.10)
transform: true + @Type (query string tur — 2.6)3. Sintaksis — tez ma'lumotnoma
// DTO + class-validator (2.1, 2.3)
export class CreateUserDto {
@IsString() @MinLength(2) ism: string;
@IsEmail() email: string;
@IsOptional() @IsInt() yosh?: number;
}
// ValidationPipe (2.4, 2.5)
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
// Mapped types (2.7)
class UpdateUserDto extends PartialType(CreateUserDto) {}
// Nested 2.8-bob: @ValidateNested() @Type(() => Dto)
// Built-in pipe 2.10-bob: @Param("id", ParseIntPipe) id: number
// Response 2.13-bob: @Exclude() parol;4. Batafsil kod namunalari
Misol 1 — To'liq DTO (class-validator — 2.3)
// create-user.dto.ts
import {
IsString, IsEmail, MinLength, MaxLength, IsOptional, IsInt, Min, Max,
IsEnum, Matches, IsNotEmpty,
} from "class-validator";
export class CreateUserDto {
@IsString()
@IsNotEmpty({ message: "Ism bo'sh bo'lmasin" })
@MinLength(2, { message: "Ism kamida 2 belgi" })
@MaxLength(50)
ism: string;
@IsEmail({}, { message: "Email formati noto'g'ri" })
email: string;
@IsString()
@MinLength(8, { message: "Parol kamida 8 belgi" })
@Matches(/[A-Z]/, { message: "Katta harf kerak" }) // RegEx (2.13-JS)
@Matches(/\d/, { message: "Raqam kerak" })
parol: string;
@IsOptional()
@IsInt() @Min(18) @Max(120)
yosh?: number;
@IsOptional()
@IsEnum(["user", "admin"], { message: "Rol: user yoki admin" })
rol?: string;
}Misol 2 — Global ValidationPipe (xavfsiz — 2.5)
// main.ts
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // DTO'da yo'q maydon o'chiriladi (mass assignment — 14)
forbidNonWhitelisted: true, // ortiqcha maydon 400
transform: true, // tur'ga aylantirish
transformOptions: { enableImplicitConversion: true },
}),
);
await app.listen(3000);
}
bootstrap();
// Endi har @Body() DTO avtomatik tekshiriladi (har endpoint qo'lda emas)Misol 3 — Mapped types (Create Update Login — 2.7)
import { PartialType, PickType, OmitType } from "@nestjs/mapped-types";
import { CreateUserDto } from "./create-user.dto";
// Update — hamma ixtiyoriy (parol bundan tashqari)
export class UpdateUserDto extends PartialType(
OmitType(CreateUserDto, ["parol"]), // parolsiz, qolgani ixtiyoriy (alohida endpoint)
) {}
// Login — faqat email/parol
export class LoginDto extends PickType(CreateUserDto, ["email", "parol"]) {}
// Parol o'zgartirish — faqat parol
export class ChangePasswordDto extends PickType(CreateUserDto, ["parol"]) {
@IsString() @MinLength(8) eskiParol: string; // qo'shimcha maydon
}
// Bir Create DTO'dan butun oila (validatsiya saqlanadi — DRY)Misol 4 — Nested validation (buyurtma — 2.8)
import { ValidateNested, IsArray, ArrayMinSize, IsNumber, Min } from "class-validator";
import { Type } from "class-transformer";
export class OrderItemDto {
@IsNumber() productId: number;
@IsNumber() @Min(1) miqdor: number;
}
export class CreateOrderDto {
@IsArray()
@ArrayMinSize(1, { message: "Kamida 1 mahsulot" })
@ValidateNested({ each: true }) // har element tekshiriladi (2.8)
@Type(() => OrderItemDto) // transform (class-transformer)
items: OrderItemDto[];
@IsOptional() @IsString()
izoh?: string;
}
// items[].productId, items[].miqdor — har biri tekshiriladi (8.4: Misol 7)Misol 5 — Query DTO + transform (2.6)
import { IsOptional, IsInt, Min, Max, IsString, IsIn } from "class-validator";
import { Type } from "class-transformer";
export class FilterDto {
@IsOptional()
@Type(() => Number) // string number (query — 2.6)
@IsInt() @Min(1)
page: number = 1;
@IsOptional()
@Type(() => Number)
@IsInt() @Min(1) @Max(100) // max 100 (DoS — 14)
limit: number = 20;
@IsOptional() @IsString()
qidiruv?: string;
@IsOptional() @IsIn(["narx", "createdAt"]) // faqat shu qiymatlar
sort?: string;
}
@Get()
hammasi(@Query() filter: FilterDto) { // page/limit — number (transform)
return this.service.hammasi(filter);
}Misol 6 — Built-in pipe'lar (2.10)
import { Controller, Get, Param, Query, ParseIntPipe, ParseUUIDPipe, DefaultValuePipe } from "@nestjs/common";
@Controller("users")
export class UsersController {
@Get(":id")
bitta(@Param("id", ParseIntPipe) id: number) { // string number (yoki 400)
return this.service.bitta(id); // id — number (kafolat)
}
@Get("uuid/:uuid")
byUuid(@Param("uuid", ParseUUIDPipe) uuid: string) { // UUID tekshiruvi
return this.service.byUuid(uuid);
}
@Get()
hammasi(
@Query("page", new DefaultValuePipe(1), ParseIntPipe) page: number, // default + parse
@Query("limit", new DefaultValuePipe(20), ParseIntPipe) limit: number,
) {
return this.service.hammasi(page, limit);
}
}Misol 7 — Custom pipe (2.11)
import { PipeTransform, Injectable, BadRequestException, ArgumentMetadata } from "@nestjs/common";
// Trim pipe (sanitatsiya — 5.9)
@Injectable()
export class TrimPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (typeof value === "object" && value !== null) {
// body obyekt maydonlarini trim
Object.keys(value).forEach((k) => {
if (typeof value[k] === "string") value[k] = value[k].trim();
});
}
return value;
}
}
// MongoDB ObjectId pipe (6.2)
@Injectable()
export class ParseObjectIdPipe implements PipeTransform {
transform(value: string) {
if (!mongoose.isValidObjectId(value)) {
throw new BadRequestException(`Noto'g'ri ID: ${value}`); // (8.1)
}
return value;
}
}
// Ishlatish
@Get(":id")
bitta(@Param("id", ParseObjectIdPipe) id: string) {
return this.service.bitta(id);
}Misol 8 — Response DTO (parolsiz — 2.13, 14)
import { Exclude, Expose, Type } from "class-transformer";
export class UserResponseDto {
@Expose() id: number;
@Expose() ism: string;
@Expose() email: string;
@Exclude() parol: string; // javobda YO'Q (14, 5.15)
@Expose()
@Type(() => Date)
createdAt: Date;
constructor(partial: Partial<UserResponseDto>) {
Object.assign(this, partial);
}
}// Servisda plainToInstance bilan Response DTO yasash (2.6 — pipe'siz)
import { plainToInstance } from "class-transformer";
async bitta(id: number): Promise<UserResponseDto> {
const user = await this.repo.findOneBy({ id }); // entity (parol bor)
return plainToInstance(UserResponseDto, user, {
excludeExtraneousValues: true, // faqat @Expose maydonlar (parol chiqmaydi — 14)
});
}
// excludeExtraneousValues: true — @Expose'siz maydonlar butunlay olib tashlanadi (qat'iy)// main.ts — ClassSerializerInterceptor global (8.7)
import { ClassSerializerInterceptor } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
// Controller — entity'da @Exclude (yoki Response DTO qaytarish)
@Get(":id")
bitta(@Param("id", ParseIntPipe) id: number) {
return this.service.bitta(id); // @Exclude maydonlar avtomatik olib tashlanadi
}Misol 9 — To'liq controller (DTO bilan — 2.4)
@Controller("users")
export class UsersController {
constructor(private usersService: UsersService) {}
@Post()
yarat(@Body() dto: CreateUserDto) { // ValidationPipe avtomatik tekshiradi (2.4)
return this.usersService.yarat(dto);
}
@Get()
hammasi(@Query() filter: FilterDto) { // query DTO (transform — 2.6)
return this.usersService.hammasi(filter);
}
@Get(":id")
bitta(@Param("id", ParseIntPipe) id: number) { // built-in pipe (2.10)
return this.usersService.bitta(id);
}
@Patch(":id")
yangila(@Param("id", ParseIntPipe) id: number, @Body() dto: UpdateUserDto) { // PartialType (2.7)
return this.usersService.yangila(id, dto);
}
}
// Har endpoint — DTO + pipe (toza, type-safe, validatsiyalangan)Misol 10 — Validatsiya xato javobi (5.10)
Noto'g'ri ma'lumot yuborilsa (ValidationPipe 400):
POST /users { "email": "xato", "parol": "123" }
Javob (avtomatik — 5.7):
{
"statusCode": 400,
"message": [
"Email formati noto'g'ri",
"Parol kamida 8 belgi",
"Katta harf kerak"
],
"error": "Bad Request"
}
Frontend 11.10-bob bu xabarlarni ko'rsatadi (foydalanuvchiga)5. To'g'ri va noto'g'ri holatlar
1) Entity'ni DTO o'rniga
// entity to'g'ridan (validatsiyasiz, ortiqcha maydon — 2.2)
@Post() yarat(@Body() user: User) {}
// DTO
@Post() yarat(@Body() dto: CreateUserDto) {}2) whitelist'siz ValidationPipe
// ortiqcha maydon o'tadi (mass assignment — 14, 2.5)
new ValidationPipe()
// whitelist
new ValidationPipe({ whitelist: true, transform: true })3) DTO interface (class emas)
// interface — dekorator ishlamaydi (runtime'da yo'q — 7.1, 2.1)
interface CreateUserDto {}
// class
class CreateUserDto { @IsEmail() email: string; }4) Update DTO qo'lda takrorlash
// Create'ni qo'lda takror (sinxronsiz — 2.7)
class UpdateUserDto { @IsString() ism?: string; ... }
// PartialType
class UpdateUserDto extends PartialType(CreateUserDto) {}5) Parolni javobda
// parol qaytadi (14, 2.13)
return user;
// @Exclude / Response DTO6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Validatsiya ishlamaydi (DTO tekshirilmaydi)
Sababi: ValidationPipe global emas, yoki DTO class emas (2.1, 2.4). Yechimi: useGlobalPipes(new ValidationPipe()); DTO — class.
Xato 2 — Query/param string (number kutilgan)
Sababi: transform yo'q 2.6-bob. Yechimi: transform: true + @Type(() => Number); yoki ParseIntPipe.
Xato 3 — Nested obyekt tekshirilmaydi
Sababi: @ValidateNested/@Type yo'q 2.8-bob. Yechimi: @ValidateNested() + @Type(() => Dto).
Xato 4 — Ortiqcha maydon DB'ga ketdi
Sababi: whitelist yo'q (2.5, 14). Yechimi: whitelist: true.
Xato 5 — class-validator dekorator ishlamaydi
Sababi: class-validator/transformer o'rnatilmagan, yoki tsconfig (7.6: dekorator). Yechimi: npm i class-validator class-transformer; experimentalDecorators.
Xato 6 — PartialType validatsiya yo'q
Sababi: noto'g'ri paket (@nestjs/mapped-types yoki @nestjs/swagger). Yechimi: to'g'ri import; Swagger uchun @nestjs/swagger.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Validatsiya 5.9-bob: "foydalanuvchiga ishonmang" — NestJS'da.
- class-validator/decorators 7.6-bob: dekorator validatsiya.
- Mapped types 7.4-bob: PartialType/Pick/Omit.
- Request lifecycle (8.1: 2.12): pipe.
- Relations 8.4-bob: nested DTO.
- Swagger 8.8-bob: DTO + @ApiProperty.
- Error handling 8.7-bob: validatsiya xatosi.
- Auth 8.9-bob: login/signup DTO.
- Frontend 11.10-bob: validatsiya xabarlari.
- Xavfsizlik (14): mass assignment, sanitatsiya.
8. Eng yaxshi amaliyotlar (best practices)
- DTO entity'dan alohida (kirish/chiqish — 2.2).
- Global ValidationPipe (whitelist + transform + forbidNonWhitelisted — 2.5, 14).
- class-validator dekoratorlar (har maydon — 2.3); DTO — class 2.1-bob.
- Mapped types (Create Update — DRY — 2.7).
- Nested validation (ichma-ich — @ValidateNested — 2.8).
- Response DTO (@Exclude — parol — 14, 2.13).
- Built-in pipe (ParseIntPipe — DTO'siz parametr — 2.10).
- transform: true + @Type (query tur — 2.6).
- Custom pipe (maxsus mantiq — 2.11).
- DTO + Swagger (bir manba — 8.8, 2.14).
9. Amaliy loyiha: "Validatsiya Qatlami (DTO + Pipes)"
NestJS validatsiyasini mustahkamlash.
Maqsad
To'liq DTO va validatsiya qatlamini qurish: class-validator, ValidationPipe, mapped types, nested, custom pipe, response DTO.
Talablar (requirements)
- DTO'lar: CreateUserDto, CreateOrderDto — class-validator (Misol 1, 2.3).
- Global ValidationPipe: whitelist + transform + forbidNonWhitelisted (Misol 2, 2.5).
- Mapped types: Update (PartialType), Login (PickType) — Misol 3, 2.7.
- Nested validation: buyurtma items (Misol 4, 2.8).
- Query DTO: filter (transform — page/limit number — Misol 5, 2.6).
- Built-in pipe: ParseIntPipe, DefaultValuePipe (Misol 6, 2.10).
- Custom pipe: Trim yoki ParseObjectId (Misol 7, 2.11).
- Response DTO: @Exclude parol (Misol 8, 2.13, 14).
- Xato javobi: validatsiya xatolari (Misol 10).
- (Bonus) Swagger: @ApiProperty (8.8, 2.14).
Maslahatlar (hint)
- DTO — class (interface emas — 2.1, 3-xato).
- ValidationPipe whitelist + transform (2.5, 4-xato).
- Query @Type(() => Number) (2.6, 2-xato).
- Nested @ValidateNested + @Type (2.8, 3-xato).
- PartialType (Update — 2.7, 4-xato).
- @Exclude (parol — 14, 2.13).
"Tayyor" mezonlari (acceptance criteria)
- DTO'lar (class-validator dekoratorlar).
- Global ValidationPipe (whitelist/transform).
- Mapped types (Update/Login).
- Nested validation (items).
- Query DTO (transform).
- Built-in pipe.
- Custom pipe.
- Response DTO (parolsiz).
- Noto'g'ri ma'lumot 400 (xato xabarlari).
- (Bonus) Swagger.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda NestJS'da kiruvchi ma'lumotni tekshirishni o'rgandik:
- DTO (ma'lumot shakli — 2.1); entity'dan alohida (kirish/chiqish — 2.2); class-validator (dekorator qoidalar — 2.3).
- ValidationPipe (avtomatik tekshiruv — 2.4); opsiyalar (whitelist/transform/forbidNonWhitelisted — xavfsizlik — 2.5); transform 2.6-bob.
- Mapped types (PartialType/Pick/Omit — DRY — 2.7); nested validation 2.8-bob.
- Pipe (transform/validate — 2.9); built-in (ParseIntPipe — 2.10); custom pipe 2.11-bob; Response DTO (@Exclude — parol — 14, 2.13); Swagger 2.14-bob.
Keyingi bob — 8.6-bob: Guards, Interceptors, Middleware, Exception Filters (chuqur). DTO/validatsiyani bildik; endi NestJS'ning request lifecycle (8.1: 2.12) qatlamlarini — guards (auth/RBAC), interceptors (transform/log/kesh), middleware, exception filters (xato) — chuqur o'rganamiz. Bu — NestJS'ning eng kuchli, "cross-cutting" mexanizmlari (auth, log, xato — bir joyda).
Foydalanilgan rasmiy/ishonchli manbalar
- docs.nestjs.com/techniques/validation; /pipes (ValidationPipe, built-in pipes, custom)
- docs.nestjs.com — Mapped types (PartialType/PickType/OmitType); class-validator, class-transformer
- Medium/DEV — NestJS validation with class-validator 2026 (whitelist, transform, nested)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!