Pipes — Built-in, Custom, Validation

~20 daq · NestJS · Boshlang'ich daraja

Pipe nima?

Tasavvur qiling: siz tog'dan oqib tushadigan suvni uyingizga olib kelmoqchisiz. Lekin o'sha suv toza emas — tosh, loy aralash. Shuning uchun quvurga filtr o'rnatiladi. Suv filtrdan o'tadi, toza holda kranigizga yetib keladi.

NestJS da Pipe aynan shu filtr. Foydalanuvchidan kelgan ma'lumot controller ga yetib borishidan oldin Pipe dan o'tadi. Pipe ikki ish qiladi:

Transformation — ma'lumotni o'zgartiradi. "42" stringni 42 songa aylantiradi.

Validation — ma'lumotni tekshiradi. Noto'g'ri bo'lsa — xato qaytaradi, controller ga yetkazmayin.


Built-in Pipes — tayyor Pipe lar

NestJS o'zi bir nechta tayyor Pipe beradi. Ularni import qilib ishlatish kifoya.

ParseIntPipe

URL parametrdan kelgan string ni number ga aylantiradi:

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

@Controller("users")
export class UsersController {
  @Get(":id")
  findOne(@Param("id", ParseIntPipe) id: number) {
    return `${id}-foydalanuvchi`; // id bu yerda number
  }
}

/users/42 — ishlaydi, id = 42. /users/abc — ParseIntPipe xato qaytaradi:

{
  "statusCode": 400,
  "message": "Validation failed (numeric string is expected)",
  "error": "Bad Request"
}

Controller ga umuman yetib bormaydi.

ParseUUIDPipe

Agar ID sifatida UUID ishlatayotgan bo'lsangiz:

@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
  return `UUID: ${id}`;
}

/users/550e8400-e29b-41d4-a716-446655440000 — ishlaydi. /users/123 — xato. UUID formatida emas.

ParseBoolPipe

@Get()
findAll(@Query('active', ParseBoolPipe) active: boolean) {
  return `Faol: ${active}`;
}

/users?active=true → active = true (boolean). /users?active=noto'g'ri → xato.

DefaultValuePipe

Query parametr kelmasа — standart qiymat beradi:

@Get()
findAll(
  @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
  @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
) {
  return `${page}-sahifa, ${limit} ta`;
}

/users — page = 1, limit = 10. /users?page=3 — page = 3, limit = 10.

DefaultValuePipe avval standart qiymat beradi, keyin ParseIntPipe songa aylantiradi. Tartib muhim.

ValidationPipe

Oldingi darsda main.ts da global qo'ygan edik. Lekin uni alohida endpoint uchun ham ishlatish mumkin:

@Post()
create(@Body(new ValidationPipe()) createUserDto: CreateUserDto) {
  return this.usersService.create(createUserDto);
}

Pipe larni qayerga qo'yish mumkin

Pipe lar uch joyda ishlashi mumkin. Har birining o'z maqsadi bor.

Parametr darajasida — faqat bir parametrga:

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {}

Metod darajasida — butun metod uchun:

import { Post, Body, UsePipes, ValidationPipe } from "@nestjs/common";

@Post()
@UsePipes(new ValidationPipe())
create(@Body() dto: CreateUserDto) {}

Global darajasida — butun ilova uchun:

// main.ts
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));

Real loyihalarda ValidationPipe ni global qo'yish eng qulay. ParseIntPipe kabilarni esa kerakli parametrga alohida.


Custom Pipe — o'zingizning Pipe

Ba'zan tayyor Pipe lar yetarli bo'lmaydi. Masalan, telefon raqamni tekshirish yoki o'zbekiston pochta indeksini validatsiya qilish — bular uchun o'z Pipe ingizni yozasiz.

CLI bilan yaratish:

nest g pipe common/pipes/phone --no-spec

--no-spec — test faylini yaratmaslik uchun. Fayl src/common/pipes/phone.pipe.ts manzilida yaratiladi.

// common/pipes/phone.pipe.ts
import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  BadRequestException,
} from "@nestjs/common";

@Injectable()
export class PhonePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    // Bo'shliqlarni olib tashlaymiz
    const cleaned = value.replace(/\s/g, "");

    // O'zbek telefon raqami formati: +998XXXXXXXXX
    const phoneRegex = /^\+998[0-9]{9}$/;

    if (!phoneRegex.test(cleaned)) {
      throw new BadRequestException(
        "Telefon raqam noto'g'ri. Format: +998XXXXXXXXX",
      );
    }

    return cleaned; // tozalangan qiymatni qaytaramiz
  }
}

PipeTransform — bu interfeys. Uni implement qilganda transform metodini yozish shart. Bu metod:

  • value — kelgan qiymat
  • metadata — qo'shimcha ma'lumot (parametr turi va h.k.)
  • return qilingan qiymat — keyingi bosqichga uzatiladi

Ishlatish:

@Post()
create(@Body('phone', PhonePipe) phone: string) {
  return `Telefon: ${phone}`;
}

Custom Pipe — transformation misoli

Validation emas, transformation — ma'lumotni o'zgartirish uchun Pipe:

// common/pipes/trim.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata } from "@nestjs/common";

@Injectable()
export class TrimPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    // Faqat stringlarni tozalaymiz
    if (typeof value !== "string") {
      return value;
    }

    return value.trim(); // bosh va oxirdagi bo'shliqlarni olib tashlaymiz
  }
}

Yoki butun object dagi barcha string maydonlarni trim qilish:

@Injectable()
export class TrimObjectPipe implements PipeTransform {
  transform(value: any) {
    if (typeof value !== "object" || value === null) {
      return value;
    }

    return Object.keys(value).reduce((acc, key) => {
      acc[key] =
        typeof value[key] === "string" ? value[key].trim() : value[key];
      return acc;
    }, {});
  }
}

Foydalanuvchi " Jasur " yuborsa — "Jasur" bo'lib keladi.


Pipe lar ketma-ketligi

Bir parametrga bir nechta Pipe qo'yish mumkin. Ular chapdan o'ngga ishlaydi:

@Get(':id')
findOne(
  @Param('id', TrimPipe, ParseIntPipe) id: number
) {
  return id;
}

Avval TrimPipe — bo'shliqlarni olib tashlaydi. Keyin ParseIntPipe — stringni songa aylantiradi.


Xato xabarini sozlash

ParseIntPipe ning standart xato xabari inglizcha. O'zbek tilida bermoqchi bo'lsangiz:

@Get(':id')
findOne(
  @Param('id', new ParseIntPipe({
    exceptionFactory: () =>
      new BadRequestException('ID butun son bo\'lishi kerak'),
  }))
  id: number,
) {
  return id;
}

Hammasi birgalikda

Keling, real loyihadagi endpoint ko'ramiz — sahifalash bilan foydalanuvchilar ro'yxati:

import {
  Controller,
  Get,
  Param,
  Query,
  ParseIntPipe,
  DefaultValuePipe,
  ParseUUIDPipe,
} from "@nestjs/common";

@Controller("users")
export class UsersController {
  // Sahifalash bilan ro'yxat
  @Get()
  findAll(
    @Query("page", new DefaultValuePipe(1), ParseIntPipe) page: number,
    @Query("limit", new DefaultValuePipe(10), ParseIntPipe) limit: number,
  ) {
    return {
      page,
      limit,
      data: [],
    };
  }

  // ID bilan bitta foydalanuvchi
  @Get(":id")
  findOne(@Param("id", ParseIntPipe) id: number) {
    return { id };
  }
}

Xulosa

Pipe — bu ma'lumotning "filtr va o'zgartiruvchi" si. Controller ga noto'g'ri yoki xom ma'lumot yetib bormasin deyishingiz uchun Pipe ishlatiladi.

Eslab qoling:

  • ParseIntPipe → string → number
  • ParseUUIDPipe → UUID formatini tekshiradi
  • ParseBoolPipe → string → boolean
  • DefaultValuePipe → standart qiymat beradi
  • ValidationPipe → DTO validatsiyasi
  • Custom Pipe → PipeTransform implement qiling, transform metodini yozing
  • Pipe lar chapdan o'ngga ketma-ket ishlaydi

Keyingi dars

7-dars: Exception Filters va Error Handling