NestJS

Routing va DTO

Har qanday REST API'ning yuragi โ€” bu routing, ya'ni turli manzil va HTTP metodlarini tegishli funksiyalarga bog'lash. NestJS routing'ni juda tushunarli qiladi: har bir metod uchun alohida dekorator, parametrlarni olish uchun alohida dekorator. Bundan tashqari, kiruvchi ma'lumotni DTO klasslari va avtomatik validatsiya bilan xavfsiz qilishni o'rganamiz.

HTTP metod dekoratorlari

REST API'da har bir amal o'z HTTP metodiga ega bo'ladi. NestJS har bir metod uchun dekorator beradi. Ular controller metodini ma'lum bir marshrut va metodga bog'laydi:

// src/users/users.controller.ts
import { Controller, Get, Post, Put, Delete } from '@nestjs/common';

@Controller('users')
export class UsersController {

  @Get()          // GET    /users
  findAll() { return 'ro'yxat'; }

  @Post()         // POST   /users
  create() { return 'yaratildi'; }

  @Put(':id')     // PUT    /users/5
  update() { return 'yangilandi'; }

  @Delete(':id')  // DELETE /users/5
  remove() { return 'o'chirildi'; }
}
Marshrut prefiksi (@Controller('users')) va metod yo'li (@Get(':id')) birlashadi. Ya'ni @Get(':id') aslida GET /users/:id manzilini bildiradi.

@Param โ€” marshrut parametrlari

Marshrutda :id kabi qism marshrut parametri deb ataladi. Uning qiymatini olish uchun @Param dekoratoridan foydalanamiz. Masalan, GET /users/42 so'rovida id qiymati 42 bo'ladi:

import { Controller, Get, Param } from '@nestjs/common';

@Controller('users')
export class UsersController {

  @Get(':id')
  findOne(@Param('id') id: string) {
    // Diqqat: parametr doim satr (string) sifatida keladi
    return 'Foydalanuvchi ID: ' + id;
  }
}
Marshrut parametrlari har doim satr ko'rinishida keladi, hatto ular raqamga o'xshasa ham. id'ni son sifatida ishlatish uchun uni Number(id) bilan aylantiring yoki keyinroq ko'radigan ParseIntPipe'dan foydalaning.

@Query โ€” so'rov parametrlari

URL'dagi ? dan keyingi qism โ€” bu query string (so'rov parametrlari). Masalan /users?page=2&limit=10. Ularni @Query dekoratori bilan olamiz. Bu odatda filtrlash, saralash va sahifalash uchun ishlatiladi:

import { Controller, Get, Query } from '@nestjs/common';

@Controller('users')
export class UsersController {

  // GET /users?page=2&limit=10
  @Get()
  findAll(@Query('page') page: string, @Query('limit') limit: string) {
    return 'Sahifa: ' + page + ', limit: ' + limit;
  }
}

@Body โ€” so'rov tanasi

POST va PUT so'rovlarida ma'lumot odatda so'rov tanasida (body) JSON ko'rinishida yuboriladi. Uni olish uchun @Body dekoratoridan foydalanamiz. NestJS JSON'ni avtomatik o'qiydi (Express'dagi express.json() ichkarida allaqachon yoqilgan):

import { Controller, Post, Body } from '@nestjs/common';

@Controller('users')
export class UsersController {

  @Post()
  create(@Body() body: any) {
    // body โ€” bu mijoz yuborgan JSON obyekti
    return 'Yaratildi: ' + body.ism;
  }
}

Lekin body: any yozish yomon amaliyot: bu tipni yo'qotadi va ma'lumot shaklini nazorat qilmaydi. Aynan shu yerda DTO tushunchasi kirib keladi.

DTO โ€” ma'lumot uzatish obyekti

DTO (Data Transfer Object โ€” ma'lumot uzatish obyekti) โ€” bu so'rov tanasi qanday ko'rinishda bo'lishini belgilaydigan klass. U ma'lumotning shaklini (qaysi maydonlar, qaysi tiplar) rasmiylashtiradi. DTO odatda alohida faylda yoziladi:

// src/users/dto/create-user.dto.ts
export class CreateUserDto {
  ism: string;
  email: string;
  yosh: number;
}

Endi controller'da any o'rniga DTO tipini ishlatamiz. Bu tahrirlovchida avtomatik to'ldirish (autocomplete) va tip xavfsizligini beradi:

import { Controller, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('users')
export class UsersController {

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    // Endi createUserDto.ism, .email, .yosh tip bilan mavjud
    return 'Yaratildi: ' + createUserDto.ism;
  }
}
DTO va interface orasidagi farq muhim: DTO klass bo'lishi kerak, interface emas. Sababi โ€” validatsiya dekoratorlari (keyingi bo'lim) faqat klasslarda ishlaydi. TypeScript interface'lari kompilyatsiya vaqtida yo'qoladi, klasslar esa ishga tushish paytida ham mavjud bo'ladi.

class-validator bilan validatsiya

DTO'ning eng katta kuchi โ€” u avtomatik validatsiyaga imkon beradi. Buning uchun ikki paket kerak: class-validator va class-transformer. Ularni o'rnatamiz:

npm install class-validator class-transformer

Endi DTO maydonlariga validatsiya dekoratorlarini qo'shamiz. Har bir dekorator ma'lum bir qoidani tekshiradi:

// src/users/dto/create-user.dto.ts
import { IsString, IsEmail, IsInt, Min, Max, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @MinLength(2, { message: 'Ism kamida 2 harf bo'lsin' })
  ism: string;

  @IsEmail({}, { message: 'Email formati noto'g'ri' })
  email: string;

  @IsInt()
  @Min(18)
  @Max(120)
  yosh: number;
}

Ko'p ishlatiladigan validatsiya dekoratorlari:

ValidationPipe โ€” validatsiyani yoqish

Dekoratorlarni yozishning o'zi yetarli emas โ€” NestJS'ga ularni tekshirishni buyurish kerak. Buni ValidationPipe global tarzda yoqib qilamiz. Buni main.ts'da bir marta sozlaymiz:

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Global validatsiyani yoqamiz:
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,             // DTO'da yo'q maydonlarni tashlab yuborish
    forbidNonWhitelisted: true,  // Ortiqcha maydon bo'lsa xato berish
    transform: true,             // Kelgan ma'lumotni DTO tipiga aylantirish
  }));

  await app.listen(3000);
}
bootstrap();

Endi agar mijoz noto'g'ri ma'lumot yuborsa (masalan, yosh o'rniga 15 yoki email o'rniga axlat), NestJS avtomatik 400 Bad Request xatosini qaytaradi. Siz controller'da hech qanday if tekshiruvi yozishingiz shart emas:

// Mijoz shu ma'lumotni yuborsa:
// { "ism": "A", "email": "xato", "yosh": 15 }

// NestJS avtomatik 400 xato qaytaradi:
// {
//   "statusCode": 400,
//   "message": [
//     "Ism kamida 2 harf bo'lsin",
//     "Email formati noto'g'ri",
//     "yosh must not be less than 18"
//   ],
//   "error": "Bad Request"
// }
whitelist: true juda foydali xavfsizlik sozlamasi: u DTO'da e'lon qilinmagan barcha maydonlarni avtomatik olib tashlaydi. Bu mijoz ortiqcha maydonlar (masalan, isAdmin: true) yuborib, tizimni chalg'itishining oldini oladi.

ParseIntPipe โ€” parametr aylantirish

Yodingizda bo'lsa, marshrut parametrlari doim satr keladi. NestJS'ning tayyor Pipe'lari qiymatni avtomatik aylantirib, tekshirib beradi. Masalan ParseIntPipe satrni songa aylantiradi va agar bu mumkin bo'lmasa, 400 xato beradi:

import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';

@Controller('users')
export class UsersController {

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    // id endi haqiqiy son (number), aylantirish shart emas
    // Agar /users/abc so'ralsa, avtomatik 400 xato qaytadi
    return 'ID: ' + id;
  }
}

Xulosa