DTO + Class-validator + Class-transformer
Muammodan boshlaylik
Siz foydalanuvchi ro'yxatdan o'tish uchun endpoint yozdingiz. Frontend dan ma'lumot keladi va siz uni saqlamoqchisiz. Shoshib qolsangiz, shunday yozib qo'yishingiz mumkin:
@Post()
create(@Body() body: any) {
return this.usersService.create(body);
}
Ishlaydi. Lekin bir kuni kimdir quyidagini yuboradi:
{
"name": "",
"email": "bu-email-emas",
"age": -5,
"role": "admin"
}
Nima bo'ladi? Siz bo'sh nom, noto'g'ri email, manfiy yosh va ruxsatsiz admin rolini ma'lumotlar bazasiga saqlab qo'yasiz. Bu — jiddiy muammo.
Aynan shu muammoni hal qilish uchun DTO va Validation kerak.
DTO nima?
DTO — Data Transfer Object. Bu — "qanday ma'lumot kelishi kerak" ni tasvirlovchi oddiy sinf.
Hayotiy misol: siz bankka borasiz. Kredit olish uchun sizdan anket to'ldirishingizni so'rashadi. Anketa — bu DTO. Unda qaysi maydonlar bo'lishi, qaysilari majburiy ekanligi aniq yozilgan. Noto'g'ri to'ldirsangiz — qabul qilishmaydi.
NestJS da DTO xuddi shunday ishlaydi.
O'rnatish
npm install class-validator class-transformer
Keyin main.ts ga global ValidationPipe qo'shamiz:
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // ruxsat etilmagan maydonlarni o'chiradi
forbidNonWhitelisted: true, // ruxsatsiz maydon kelsa xato beradi
transform: true, // turlarni avtomatik o'zgartiradi
}),
);
await app.listen(3000);
}
bootstrap();
Bu uch sozlama haqida pastda batafsil gaplashamiz.
Birinchi DTO
users papkasida dto papka yarating:
users/
├── dto/
│ ├── create-user.dto.ts
│ └── update-user.dto.ts
├── users.controller.ts
├── users.module.ts
└── users.service.ts
// users/dto/create-user.dto.ts
import {
IsString,
IsEmail,
IsInt,
IsEnum,
MinLength,
MaxLength,
Min,
Max,
IsNotEmpty,
} from "class-validator";
export enum UserRole {
USER = "user",
MODERATOR = "moderator",
}
export class CreateUserDto {
@IsNotEmpty({ message: "Ism bo'sh bo'lmasligi kerak" })
@IsString({ message: "Ism matn bo'lishi kerak" })
@MinLength(2, { message: "Ism kamida 2 ta harf" })
@MaxLength(50, { message: "Ism ko'pi bilan 50 ta harf" })
name: string;
@IsNotEmpty({ message: "Email bo'sh bo'lmasligi kerak" })
@IsEmail({}, { message: "Email noto'g'ri formatda" })
email: string;
@IsInt({ message: "Yosh butun son bo'lishi kerak" })
@Min(18, { message: "Yosh kamida 18 bo'lishi kerak" })
@Max(100, { message: "Yosh ko'pi bilan 100 bo'lishi kerak" })
age: number;
@IsEnum(UserRole, {
message: "Rol faqat user yoki moderator bo'lishi mumkin",
})
role: UserRole;
}
Har bir maydon ustida decorator lar — bu validatsiya qoidalari. Agar kelgan ma'lumot bu qoidalarga uymasa — NestJS avtomatik xato qaytaradi, hatto controller ga ham yetib bormaydi.
Controller da DTO ishlatish
// users/users.controller.ts
import { Controller, Post, Body } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UsersService } from "./users.service";
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
}
@Body() body: any o'rniga endi @Body() createUserDto: CreateUserDto yozdik. Shu bilan NestJS validatsiya qilishni biladi.
Endi noto'g'ri ma'lumot yuborsangiz — avtomatik bunday xato keladi:
{
"statusCode": 400,
"message": ["Email noto'g'ri formatda", "Yosh kamida 18 bo'lishi kerak"],
"error": "Bad Request"
}
Siz hech narsa yozmadingiz — NestJS o'zi tekshirdi, o'zi xato qaytardi.
whitelist va forbidNonWhitelisted
main.ts da yozgan ikki sozlamani eslaymiz:
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
});
whitelist: true — DTO da ko'rsatilmagan maydonlarni o'chiradi. Masalan, foydalanuvchi quyidagini yubborsa:
{
"name": "Jasur",
"email": "jasur@example.com",
"age": 25,
"role": "user",
"isAdmin": true,
"password": "12345"
}
isAdmin va password — DTO da yo'q. whitelist: true ularni avtomatik o'chiradi. Sizning kodingizga faqat name, email, age, role yetib keladi.
forbidNonWhitelisted: true — o'chirish o'rniga xato qaytaradi:
{
"statusCode": 400,
"message": [
"property isAdmin should not exist",
"property password should not exist"
]
}
Real loyihalarda forbidNonWhitelisted: true yaxshiroq — foydalanuvchi ortiqcha narsa yuboryaptimi, bu haqida xabar berasiz.
transform: true — avtomatik tur o'zgartirish
transform: true sozlamasi juda qulay. Misol: URL dan kelgan parametrlar har doim string. Lekin siz number kutayapsiz.
@Get(':id')
findOne(@Param('id') id: number) { // number kutayapmiz
return this.usersService.findOne(id);
}
transform: true bo'lmasa — id string bo'lib keladi, +id yozishga majbur bo'lardingiz. transform: true bilan — NestJS avtomatik string → number ga o'zgartiradi.
Update DTO — PartialType
Foydalanuvchini yangilayotganda barcha maydonlar majburiy emas. Faqat ismni o'zgartirmoqchi bo'lsam — faqat name yuborsam yetarli.
Buning uchun CreateUserDto ni qayta yozish shart emas. NestJS PartialType beradi:
// users/dto/update-user.dto.ts
import { PartialType } from "@nestjs/mapped-types";
import { CreateUserDto } from "./create-user.dto";
export class UpdateUserDto extends PartialType(CreateUserDto) {}
PartialType — CreateUserDto dagi barcha maydonlarni ixtiyoriy qilib beradi. Hammasi shu — bir qator. Validatsiya qoidalari saqlanadi, faqat majburiylik yo'qoladi.
npm install @nestjs/mapped-types
Class-transformer — response ni tozalash
Foydalanuvchi ma'lumotini qaytarayotganda parolni ko'rsatmaslik kerak. class-transformer ning Exclude decorator i yordam beradi:
// users/entities/user.entity.ts
import { Exclude } from "class-transformer";
export class UserEntity {
id: number;
name: string;
email: string;
@Exclude() // ← response da ko'rinmaydi
password: string;
constructor(partial: Partial<UserEntity>) {
Object.assign(this, partial);
}
}
Controller da ClassSerializerInterceptor qo'shamiz:
// users/users.controller.ts
import {
Controller,
Get,
Param,
UseInterceptors,
ClassSerializerInterceptor,
} from "@nestjs/common";
import { UserEntity } from "./entities/user.entity";
@Controller("users")
@UseInterceptors(ClassSerializerInterceptor)
export class UsersController {
@Get(":id")
findOne(@Param("id") id: number) {
return new UserEntity({
id: 1,
name: "Jasur",
email: "jasur@example.com",
password: "yashirin_parol", // bu ko'rinmaydi
});
}
}
Response da foydalanuvchi faqat shuni ko'radi:
{
"id": 1,
"name": "Jasur",
"email": "jasur@example.com"
}
password yo'q. Siz uni o'chirmadingiz — @Exclude() o'zi hal qildi.
Eng ko'p ishlatiladigan validatorlar
// String lar uchun
@IsString()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
@Matches(/^[a-zA-Z]+$/, { message: 'Faqat harflar' })
// Sonlar uchun
@IsNumber()
@IsInt()
@Min(0)
@Max(100)
@IsPositive()
// Boshqalar
@IsEmail()
@IsUrl()
@IsBoolean()
@IsEnum(MyEnum)
@IsArray()
@IsDate()
@IsOptional() // maydon ixtiyoriy, lekin kelsa — tekshiriladi
Xulosa
DTO — bu loyihangizdagi "shartnoma". Foydalanuvchi bilan qanday ma'lumot almashish kerakligi aniq belgilanadi. Bu shartnomaga uymagan ma'lumot — ichkariga kirmayin.
Eslab qoling:
class-validator→ validatsiya qoidalari (@IsEmail,@MinLengthva h.k.)class-transformer→ ma'lumotni o'zgartirish (@Exclude— yashirish)ValidationPipe→main.tsda bir marta — hamma joyda ishlaydiwhitelist: true→ ortiqcha maydonlar o'chiriladiPartialType→ yangilash uchun — barcha maydon ixtiyoriy
Keyingi dars
6-dars: Pipes — Built-in, Custom, Validation