WisarWisar
Dasturlash kitobi/8-QISM — NestJS23 daqiqa

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:

ts
// 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

text
  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):

bash
npm install class-validator class-transformer
ts
import { 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):

text
  ── 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)
ts
// @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 + @MaxLength o'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 matnni Date obyektiga aylantiradi, keyin @IsDate tekshiradi). Dekoratorlar birga qo'llanadi (tur + uzunlik + format), har biri alohida xato xabari.

2.4. ValidationPipe (avtomatik tekshiruv)

ValidationPipe — DTO'ni avtomatik tekshiradi:

ts
// 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)

ts
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.

disableErrorMessagestrue bo'lsa, javobda batafsil validatsiya xabarlari (message massivi — Misol 10) berilmaydi, faqat Bad Request qoladi. 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. Odatda false (xabarlar kerak) — lekin maxfiy API'da process.env.NODE_ENV === "production" bo'lsa true qilinadi. Yana ikki opsiya: stopAtFirstError: true (birinchi xatoda to'xtaydi — barcha xatolar emas), skipMissingProperties: true (yo'q maydonlarni tekshirmaydi — PATCH uchun, lekin @IsOptional aniqroq).

2.6. Transform (tur aylantirish)

ts
// 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 @Type ishonchliroq). 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:

ts
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.com va TEST@x.COM bir 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:

ts
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 nomi plainToClass) — oddiy JSON obyektni DTO instance'ga aylantiradi (@Type, @Transform, @Exclude shu 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):

ts
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 — masalan Pagination + Search bitta query DTO). Validatsiya dekoratorlari saqlanadi (Create'dagi @IsEmail Update'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)

ts
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:

text
  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, ...)

ts
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):

ts
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, yoki errorHttpStatusCode bilan boshqa kod). Bular — bitta parametr uchun; ko'p maydonli obyekt uchun — DTO + ValidationPipe.

2.11. Custom pipe (o'z pipe'ing)

ts
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 pipePipeTransform interface (transform metod). 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

ts
// 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)

ts
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 + @Excludechiqishni nazorat (kirish DTO — kelishni; response — ketishni). @Exclude() (parol) — javobda olib tashlanadi (14, 5.15). ClassSerializerInterceptor 8.7-bob — buni avtomatik qiladi. Maxfiy ma'lumot (parol) hech qachon chiqmaydi.

2.14. Validatsiya + Swagger (8.8 ko'prik)

ts
// @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/swagger mapped types (PartialType shu 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)

text
   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

ts
// 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)

ts
// 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)

ts
// 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)

ts
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)

ts
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)

ts
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)

ts
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)

ts
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)

ts
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);
  }
}
ts
// 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)
ts
// 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)

ts
@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)

text
  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

ts
//  entity to'g'ridan (validatsiyasiz, ortiqcha maydon — 2.2)
@Post() yarat(@Body() user: User) {}

//  DTO
@Post() yarat(@Body() dto: CreateUserDto) {}

2) whitelist'siz ValidationPipe

ts
//  ortiqcha maydon o'tadi (mass assignment — 14, 2.5)
new ValidationPipe()

//  whitelist
new ValidationPipe({ whitelist: true, transform: true })

3) DTO interface (class emas)

ts
//  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

ts
//  Create'ni qo'lda takror (sinxronsiz — 2.7)
class UpdateUserDto { @IsString() ism?: string; ... }

//  PartialType
class UpdateUserDto extends PartialType(CreateUserDto) {}

5) Parolni javobda

ts
//  parol qaytadi (14, 2.13)
return user;

//  @Exclude / Response DTO

6. 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)

  1. DTO'lar: CreateUserDto, CreateOrderDto — class-validator (Misol 1, 2.3).
  2. Global ValidationPipe: whitelist + transform + forbidNonWhitelisted (Misol 2, 2.5).
  3. Mapped types: Update (PartialType), Login (PickType) — Misol 3, 2.7.
  4. Nested validation: buyurtma items (Misol 4, 2.8).
  5. Query DTO: filter (transform — page/limit number — Misol 5, 2.6).
  6. Built-in pipe: ParseIntPipe, DefaultValuePipe (Misol 6, 2.10).
  7. Custom pipe: Trim yoki ParseObjectId (Misol 7, 2.11).
  8. Response DTO: @Exclude parol (Misol 8, 2.13, 14).
  9. Xato javobi: validatsiya xatolari (Misol 10).
  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!
8.5-bob: DTO, validation pipes — Wisar