Šta su “PIPES” u Nest.js-u
Pipes u NestJS-u su specijalne komponente koje se koriste za transformaciju i validaciju podataka pre nego što stignu do kontrolera ili metoda kontrolera. Pipes su korisni kada želimo da osiguramo da podaci koje primamo u API zahtevima ispunjavaju određene uslove ili da se na specifičan način transformišu.
Pipes se koriste za:
- Validaciju podataka: Proveravaju validnost ulaznih podataka i vraćaju grešku ako podaci nisu u ispravnom formatu.
- Transformaciju podataka: Mogu modifikovati ulazne podatke pre nego što stignu do metode kontrolera.
NestJS nudi nekoliko ugrađenih pipes:
- ValidationPipe: Validacija objekata na osnovu class-validator biblioteke.
- ParseIntPipe: Pokušava da konvertuje string u broj, baca grešku ako nije moguće.
- ParseBoolPipe: Pretvara vrednosti kao što su “true” i “false” u boolean vrednosti.
- ParseArrayPipe: Proverava i validira nizove.
- ParseUUIDPipe: Validira UUID vrednosti.
Validation Pipes
Validation Pipe u NestJS-u je moćan alat za validaciju podataka unutar API zahteva. Koristi class-validator biblioteku za proveru podataka na osnovu definisanih pravila. Validation Pipe omogućava proveru da li objekti koji stižu u API zahtevima odgovaraju strukturi i tipovima definisanim u DTO (Data Transfer Object) klasama.
Instalacija potrebnih biblioteka
Pre nego što počnete sa korišćenjem Validation Pipe-a, potrebno je da u projektu instalirate dve biblioteke:
- class-validator: Omogućava definisanje i proveru pravila validacije.
- class-transformer: Transformiše ulazne podatke u instance klasa DTO-a.
Koristite sledeću komandu za instalaciju:
1 |
npm install class-validator class-transformer |
Kreiranje pipes
Za kreiranje pipes, potrebno je da:
- Kreirate novu klasu koja implementira PipeTransform interfejs.
- Implementirate metodu transform(), koja prima dva parametra:
- value: Ulazna vrednost koja dolazi iz zahteva.
- metadata: Informacije o vrednosti (opciono, sadrži podatke o tipu i dekoratorima).
Evo jednostavnog primera pipe-a koji proverava da li je vrednost broj:
1 2 3 4 5 6 7 8 9 10 11 12 |
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; @Injectable() export class ParseIntPipe implements PipeTransform { transform(value: any) { const val = parseInt(value, 10); if (isNaN(val)) { throw new BadRequestException('Validation failed: Not a number'); } return val; } } |
Korišćenje Pipes u kontroleru
Nakon kreiranja pipe-a, možemo ga koristiti u kontroleru na različitim nivoima:
a) Na nivou parametra
1 2 3 4 |
@Get(':id') findOne(@Param('id', new ParseIntPipe()) id: number) { return `User with ID ${id}`; } |
b) Na nivou metode
Da bismo primenili Validation Pipe, koristimo dekorator @UsePipes na nivou metode ili klase. Validation Pipe može automatski proveravati i validirati podatke unutar metoda kontrolera.
1 2 3 4 5 |
@UsePipes(ParseIntPipe) @Get(':id') findOne(@Param('id') id: number) { return `User with ID ${id}`; } |
c) Na nivou globalnog modula
Validation Pipe možete registrovati kao globalni pipe za celu aplikaciju, čime će se validacija automatski primenjivati na sve rute i kontrolere u aplikaciji:
1 2 3 |
const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ParseIntPipe()); await app.listen(3000); |
Na ovaj način se Validation Pipe automatski primenjuje na sve zahteve u aplikaciji, bez potrebe za pojedinačnim definisanjem u svakom kontroleru.
Razumevanje grešaka validacije
Kada validacija podataka ne uspe, Validation Pipe vraća detaljan odgovor sa objašnjenjem grešaka. Na primer, greške će izgledati ovako:
1 2 3 4 5 6 7 8 9 |
{ "statusCode": 400, "message": [ "name must be a string", "age must be an integer number", "age must not be greater than 65" ], "error": "Bad Request" } |
Ovaj odgovor je veoma koristan za klijentske aplikacije, jer pruža jasnu informaciju o tome koje polje nije prošlo validaciju i zašto.
Validation Pipe opcije za prilagođavanje ponašanja
Validation Pipe u NestJS-u ima nekoliko korisnih opcija za prilagođavanje ponašanja prilikom validacije. Ove opcije omogućavaju detaljniju kontrolu nad načinom na koji će validacija biti izvršena i pomažu pri rukovanju sa nepotrebnim ili nevalidnim podacima. U nastavku su objašnjene glavne opcije koje možete koristiti sa Validation Pipe-om.
Opcija whitelist
Opcija whitelist služi za automatsko uklanjanje svih svojstava koja nisu definisana u DTO (Data Transfer Object) klasi. Kada je ova opcija omogućena, svi dodatni podaci koji nisu eksplicitno definisani u DTO klasi biće ignorisani, čime se osigurava da samo željena svojstva budu obrađena.
Primer:
1 2 3 4 5 |
@UsePipes(new ValidationPipe({ whitelist: true })) @Post() createUser(@Body() createUserDto: CreateUserDto) { return createUserDto; } |
Ovde će whitelist automatski ukloniti sva dodatna polja koja nisu definisana u CreateUserDto. Na primer, ako pošaljete { name: ‘John’, age: 30, extra: ‘nepotrebno’ }, polje extra će biti ignorisano.
Opcija forbidNonWhitelisted
Opcija forbidNonWhitelisted radi u kombinaciji sa whitelist. Kada je omogućena, ova opcija baca grešku svaki put kada se pojavi neko polje koje nije definisano u DTO klasi, umesto da ga samo ignoriše. Ovo je korisno ako želite striktno kontrolisati podatke i sprečiti prolazak bilo kakvih dodatnih informacija koje ne pripadaju zahtevanoj strukturi.
Primer:
1 2 3 4 5 |
@UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true })) @Post() createUser(@Body() createUserDto: CreateUserDto) { return createUserDto; } |
U ovom primeru, ako pošaljete podatke { name: ‘John’, age: 30, extra: ‘nepotrebno’ }, API će baciti grešku sa porukom da polje extra nije dozvoljeno.
Opcija transform
Opcija transform omogućava automatsko prebacivanje ulaznih podataka u instance DTO klasa. Kada je omogućena, ulazni podaci će se automatski transformisati u instancu DTO klase, što omogućava lakši pristup metodama i tipovima u okviru objekta. Ovo je korisno kada, na primer, želite da radite sa određenim metodama ili validacijama unutar DTO-a nakon što su podaci transformisani.
Primer:
1 2 3 4 5 6 |
@UsePipes(new ValidationPipe({ transform: true })) @Post() createUser(@Body() createUserDto: CreateUserDto) { // createUserDto je sada instanca CreateUserDto klase return createUserDto; } |
Ovde, createUserDto će automatski biti instanca CreateUserDto klase. Ovo znači da ako pošaljete JSON podatke u zahtevu, oni će se konvertovati u instancu klase sa svim pridruženim metodama i tipovima.
Opcija transformOptions
transformOptions omogućava da dodatno prilagodite način na koji će se transformacija podataka izvršiti. Ova opcija koristi class-transformer konfiguraciju za detaljniju kontrolu, kao što je uključivanje svojstava ili postavljanje određenih pravila konverzije podataka.
Primer:
1 2 3 4 5 6 7 8 |
@UsePipes(new ValidationPipe({ transform: true, transformOptions: { enableImplicitConversion: true } })) @Post() createUser(@Body() createUserDto: CreateUserDto) { return createUserDto; } |
Ovde smo omogućili enableImplicitConversion, što omogućava implicitnu konverziju tipova. Na primer, ako je age definisan kao broj u DTO-u, ali pošaljemo vrednost kao string, Validation Pipe će automatski pokušati da konvertuje string u broj.
Opcija skipMissingProperties
Opcija skipMissingProperties omogućava da validacija preskoči svojstva koja nisu prisutna u DTO-u. Ovo je korisno za validaciju delimičnih objekata, na primer, kada radite sa ažuriranjem resursa i ne želite da zahtevate sva polja, već samo ona koja treba ažurirati.
Primer:
1 2 3 4 5 |
@UsePipes(new ValidationPipe({ skipMissingProperties: true })) @Patch(':id') updateUser(@Body() updateUserDto: UpdateUserDto) { return updateUserDto; } |
Ovde će skipMissingProperties omogućiti da se validiraju samo prisutna polja u objektu, dok će nedostajuća polja biti ignorisana.
Opcija stopAtFirstError
Kada je stopAtFirstError omogućena, validacija će se zaustaviti čim naiđe na prvu grešku. Ovo može biti korisno za povećanje performansi kada nije potrebno prikazivati sve greške, već samo prvu koja se pojavi.
Primer:
1 2 3 4 5 |
@UsePipes(new ValidationPipe({ stopAtFirstError: true })) @Post() createUser(@Body() createUserDto: CreateUserDto) { return createUserDto; } |
Ovde će se validacija zaustaviti čim naiđe na prvu grešku i odmah će vratiti odgovor klijentu sa informacijom o toj grešci.
Primeri
Primer: Validacija DTO klasa
DTO klase definišu strukturu podataka i validacione uslove koje očekujemo u zahtevima. Na primer, za registraciju korisnika možemo kreirati klasu CreateUserDto sa poljima name i age:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import { IsString, IsInt } from 'class-validator'; export class CreateUserDto { @IsString() name: string; @IsInt() age: number; } @UsePipes(new ValidationPipe()) @Post() createUser(@Body() createUserDto: CreateUserDto) { return createUserDto; } |
Primer: Custom Validation Pipe
Evo primera prilagođenog pipe-a koji proverava da li je niz duži od određene vrednosti:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; @Injectable() export class MinLengthPipe implements PipeTransform { constructor(private readonly minLength: number) {} transform(value: string) { if (value.length < this.minLength) { throw new BadRequestException(`String is too short. Minimum length is ${this.minLength}`); } return value; } } @Post() createUser(@Body('name', new MinLengthPipe(5)) name: string) { return `User name is ${name}`; } |
Primer: Validacija registracija korisnika
Ovako možemo koristiti Validation Pipe za validaciju podataka prilikom registracije korisnika:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import { IsString, IsInt, IsEmail, Length, Min } from 'class-validator'; export class RegisterUserDto { @IsString() @Length(3, 20) username: string; @IsEmail() email: string; @IsInt() @Min(18) age: number; } import { Controller, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common'; @Controller('auth') export class AuthController { @Post('register') @UsePipes(new ValidationPipe()) registerUser(@Body() registerUserDto: RegisterUserDto) { return `User ${registerUserDto.username} has been registered`; } } |
Ovaj primer validira podatke kao što su korisničko ime, email i godine. Ako neki podaci nisu validni (npr. pogrešan email ili godine manje od 18), API će vratiti grešku sa odgovarajućom porukom.