Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

DTOs i podstawowa walidacja

Ave, strażniku bram! Konsul Caesar.js wie, że bezpieczenstwo imperium zaczyna sie od kontroli tego, co wchodzi przez bramy. Nie mozemy wpuscic do naszego systemu nieprawidlowych danych - to jak wpuszczenie szpiegow do obozu legionow! Czas poznac DTOs i walidacje - tarcze ochronne naszego API.

Czym sa DTOs?

DTO (Data Transfer Object) to klasa, ktora definiuje ksztalt danych przesylanych do i z naszego API. Pomysl o DTO jak o formularzu rekrutacyjnym do legionu - kazdy nowy legionista musi wypelnic odpowiednie pola, zanim zostanie przyjety.

Bez DTO nasz kontroler przyjmuje cokolwiek - to jak otwarta brama bez strazy:

1// BEZ DTO - niebezpiecznie!
2@Post()
3create(@Body() body: any) {
4  // body moze zawierac cokolwiek... niebezpiecznie!
5  return this.legionariesService.create(body);
6}

Z DTO dokladnie definiujemy, jakie dane akceptujemy:

1// Z DTO - bezpieczne jak fort rzymski!
2@Post()
3create(@Body() createLegionaryDto: CreateLegionaryDto) {
4  return this.legionariesService.create(createLegionaryDto);
5}

Tworzenie klas DTO

Stworzmy DTO dla naszego systemu legionistow:

1// dto/create-legionary.dto.ts
2export class CreateLegionaryDto {
3  name: string;
4  rank: string;
5  province: string;
6  email: string;
7  experience: number;
8}

To jednak tylko ksztalt danych - nie sprawdza jeszcze, czy dane sa prawidlowe. Tu wkracza walidacja!

Instalacja class-validator i class-transformer

Aby dodac walidacje, potrzebujemy dwoch bibliotek:

1npm install class-validator class-transformer
  • class-validator - dostarcza dekoratory walidacji (@IsString, @IsNumber, itd.)
  • class-transformer - przeksztalca zwykle obiekty JSON na instancje klas DTO

Dekoratory walidacji

Teraz dodajmy walidacje do naszego DTO:

1// dto/create-legionary.dto.ts
2import {
3  IsString, IsNumber, IsEmail,
4  MinLength, MaxLength, Min, Max,
5  IsNotEmpty, IsOptional, IsIn
6} from 'class-validator';
7
8export class CreateLegionaryDto {
9  @IsString()
10  @IsNotEmpty()
11  @MinLength(2)
12  @MaxLength(50)
13  name: string;
14
15  @IsString()
16  @IsIn(['Miles', 'Optio', 'Centurio', 'Tribunus', 'Legatus'])
17  rank: string;
18
19  @IsString()
20  @IsNotEmpty()
21  province: string;
22
23  @IsEmail()
24  email: string;
25
26  @IsNumber()
27  @Min(0)
28  @Max(30)
29  experience: number;
30}

Oto najczesciej uzywane dekoratory walidacji:

| Dekorator | Opis | |-----------|------| |

@IsString()
| Sprawdza czy wartosc to tekst | |
@IsNumber()
| Sprawdza czy wartosc to liczba | |
@IsEmail()
| Sprawdza czy wartosc to prawidlowy email | |
@IsNotEmpty()
| Sprawdza czy pole nie jest puste | |
@MinLength(n)
| Minimalna dlugosc tekstu | |
@MaxLength(n)
| Maksymalna dlugosc tekstu | |
@Min(n)
| Minimalna wartosc liczbowa | |
@Max(n)
| Maksymalna wartosc liczbowa | |
@IsOptional()
| Pole jest opcjonalne | |
@IsIn([...])
| Wartosc musi byc jedna z podanych |

DTO do aktualizacji - PartialType

Do czesciowej aktualizacji (PATCH) mozemy uzyc

PartialType
, ktory sprawia, ze wszystkie pola sa opcjonalne:

1// dto/update-legionary.dto.ts
2import { PartialType } from '@nestjs/mapped-types';
3import { CreateLegionaryDto } from './create-legionary.dto';
4
5export class UpdateLegionaryDto extends PartialType(CreateLegionaryDto) {}
6// Teraz wszystkie pola sa opcjonalne!

PartialType
automatycznie tworzy nowa klase, w ktorej kazde pole z
CreateLegionaryDto
staje sie opcjonalne. Dzieki temu nie musimy pisac osobnego DTO z
@IsOptional()
na kazdym polu.

ValidationPipe - globalna walidacja

Aby walidacja dzialala, musimy aktywowac

ValidationPipe
w naszej aplikacji:

1// main.ts
2import { NestFactory } from '@nestjs/core';
3import { ValidationPipe } from '@nestjs/common';
4import { AppModule } from './app.module';
5
6async function bootstrap() {
7  const app = await NestFactory.create(AppModule);
8
9  app.useGlobalPipes(new ValidationPipe({
10    whitelist: true,          // Usuwa pola, ktore nie sa w DTO
11    forbidNonWhitelisted: true, // Rzuca blad gdy sa nieznane pola
12    transform: true,          // Automatyczna konwersja typow
13  }));
14
15  await app.listen(3000);
16}
17bootstrap();

Trzy kluczowe opcje

ValidationPipe
:

  • whitelist: true - automatycznie usuwa pola, ktorych nie ma w DTO (jak straznik odrzucajacy podejrzane przesylki)
  • forbidNonWhitelisted: true - zamiast cicho usuwac nieznane pola, rzuca blad (surowsza kontrola)
  • transform: true - automatycznie konwertuje dane na odpowiednie typy (string "5" na number 5)

Kompletny przyklad kontrolera z DTO

1@Controller('legionaries')
2export class LegionariesController {
3  constructor(private readonly legionariesService: LegionariesService) {}
4
5  @Post()
6  create(@Body() createDto: CreateLegionaryDto) {
7    return this.legionariesService.create(createDto);
8  }
9
10  @Patch(':id')
11  update(
12    @Param('id') id: string,
13    @Body() updateDto: UpdateLegionaryDto,
14  ) {
15    return this.legionariesService.update(Number(id), updateDto);
16  }
17}

Kiedy ktos przesle nieprawidlowe dane, ValidationPipe automatycznie zwroci czytelny blad:

1{
2  "statusCode": 400,
3  "message": [
4    "name must be longer than or equal to 2 characters",
5    "email must be an email",
6    "experience must not be greater than 30"
7  ],
8  "error": "Bad Request"
9}

Dlaczego DTOs sa wazne?

  1. Bezpieczenstwo - blokuja niechciane dane zanim dotra do logiki biznesowej
  2. Dokumentacja - jasno definiuja kontrakt API
  3. Walidacja - automatyczna weryfikacja poprawnosci danych
  4. Typowanie - TypeScript zna ksztalt danych w calej aplikacji
  5. Separacja - oddzielaja wartswe transportu od logiki biznesowej

DTOs i walidacja to tarcza i miecz naszego imperium - chronia bramy API i zapewniaja, ze do systemu trafiaja tylko prawidlowe dane. Kazdy dobry budowniczy imperium wie, ze silne mury to podstawa!

Vai a CodeWorlds