Podstawy NestJS

Wprowadzenie

NestJS to framework do tworzenia aplikacji serwerowych w Node.js, który cieszy się popularnością dzięki modularnej architekturze oraz wsparciu dla TypeScript. W tym wpisie przyjrzymy się podstawowym elementom NestJS, takim jak moduły, kontrolery i serwisy. Zrozumiemy, jak działają, jaką pełnią rolę i jak tworzyć te kluczowe elementy aplikacji. Przyjrzymy się także zarządzaniu zapytaniami HTTP, czyli obsłudze tras (routing), parametrów, i wdrożeniu walidacji za pomocą DTO. Zaczynajmy!

Moduły, kontrolery i serwisy

Moduły w NestJS

NestJS jest oparty na koncepcji modularności, co pozwala na lepsze zarządzanie kodem w dużych projektach. Moduł w NestJS jest grupą funkcjonalności, która może zawierać kontrolery, serwisy oraz inne komponenty, które są ze sobą powiązane. Głównym celem modułów jest organizacja kodu, co sprawia, że aplikacja jest bardziej przejrzysta i łatwiejsza do zarządzania.

Przykładowy moduł może wyglądać tak:

				
					import { Module } from '@nestjs/common';
import { UzytkownikController } from './uzytkownik.controller';
import { UzytkownikService } from './uzytkownik.service';

@Module({
  controllers: [UzytkownikController],
  providers: [UzytkownikService],
})
export class UzytkownikModule {}

				
			

W powyższym przykładzie UzytkownikModule rejestruje kontroler UzytkownikController oraz serwis UzytkownikService. Dzięki temu te komponenty mogą współpracować ze sobą w ramach modułu.

Kontrolery w NestJS

Kontrolery są odpowiedzialne za obsługę zapytań HTTP oraz przekazywanie danych do serwisów, które przetwarzają logikę biznesową. W NestJS kontrolery definiują trasy (routes), do których dostęp mają klienci, czyli użytkownicy aplikacji.

Poniżej przedstawiamy przykładowy kontroler:

				
					import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UzytkownikService } from './uzytkownik.service';
import { StworzUzytkownikDto } from './dto/stworz-uzytkownik.dto';

@Controller('uzytkownicy')
export class UzytkownikController {
  constructor(private readonly uzytkownikService: UzytkownikService) {}

  @Get()
  znajdzWszystkich() {
    return this.uzytkownikService.znajdzWszystkich();
  }

  @Get(':id')
  znajdzJednego(@Param('id') id: string) {
    return this.uzytkownikService.znajdzJednego(id);
  }

  @Post()
  stworz(@Body() stworzUzytkownikDto: StworzUzytkownikDto) {
    return this.uzytkownikService.stworz(stworzUzytkownikDto);
  }
}

				
			

W kontrolerze UzytkownikController definiujemy trzy różne endpointy:

  • GET /uzytkownicy: znajdź wszystkich użytkowników.
  • GET /uzytkownicy/:id: znajdź użytkownika po jego ID.
  • POST /uzytkownicy: utwórz nowego użytkownika.

Każdy endpoint wywołuje odpowiednią metodę serwisu, co pozwala na oddzielenie logiki kontrolera od logiki biznesowej.

Serwisy w NestJS

Serwisy to miejsce, gdzie umieszczamy logikę biznesową. Odpowiadają one za przetwarzanie danych oraz wykonywanie działań niezwiązanych bezpośrednio z obsługą zapytań HTTP.

Przykładowy serwis wygląda tak:

				
					import { Injectable } from '@nestjs/common';
import { StworzUzytkownikDto } from './dto/stworz-uzytkownik.dto';

@Injectable()
export class UzytkownikService {
  private readonly uzytkownicy = [];

  znajdzWszystkich() {
    return this.uzytkownicy;
  }

  znajdzJednego(id: string) {
    return this.uzytkownicy.find(uzytkownik => uzytkownik.id === id);
  }

  stworz(stworzUzytkownikDto: StworzUzytkownikDto) {
    const nowyUzytkownik = {
      id: Math.random().toString(),
      ...stworzUzytkownikDto,
    };
    this.uzytkownicy.push(nowyUzytkownik);
    return nowyUzytkownik;
  }
}

				
			

W serwisie UzytkownikService mamy trzy metody odpowiadające za znajdowanie wszystkich użytkowników, znajdowanie jednego użytkownika oraz tworzenie nowego użytkownika. Serwis pozwala oddzielić logikę przetwarzania danych od kontrolera.

Routing i zarządzanie zapytaniami HTTP

Podstawowe zapytania HTTP: GET, POST, PUT, DELETE

NestJS umożliwia tworzenie tras HTTP w bardzo prosty sposób przy użyciu kontrolerów i dekoratorów takich jak @Get(), @Post(), @Put(), @Delete().

Przykład obsługi wszystkich metod HTTP:

				
					import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { StworzUzytkownikDto } from './dto/stworz-uzytkownik.dto';

@Controller('uzytkownicy')
export class UzytkownikController {
  @Get()
  znajdzWszystkich() {
    // kod do znajdowania wszystkich użytkowników
  }

  @Get(':id')
  znajdzJednego(@Param('id') id: string) {
    // kod do znajdowania jednego użytkownika
  }

  @Post()
  stworz(@Body() stworzUzytkownikDto: StworzUzytkownikDto) {
    // kod do tworzenia użytkownika
  }

  @Put(':id')
  aktualizuj(@Param('id') id: string, @Body() stworzUzytkownikDto: StworzUzytkownikDto) {
    // kod do aktualizacji użytkownika
  }

  @Delete(':id')
  usun(@Param('id') id: string) {
    // kod do usuwania użytkownika
  }
}

				
			

W powyższym przykładzie mamy do czynienia z pełnym zestawem metod CRUD, co pozwala na operacje na danych użytkowników: ich tworzenie, odczyt, aktualizację i usuwanie.

Parametry trasy i zapytania

Kontrolery NestJS obsługują parametry tras za pomocą dekoratora @Param(). Dla danych przesyłanych w ciele zapytania używamy dekoratora @Body(). Przykłady powyżej pokazują, jak łatwo można korzystać z tych dekoratorów, aby pobierać odpowiednie informacje z zapytania.

Data Transfer Object (DTO) i walidacja danych

DTO, czyli Obiekty Transferu Danych, są używane do definiowania kształtu danych, które są przesyłane do aplikacji. Dzięki nim możemy zapewnić, że nasze API otrzymuje dane w odpowiednim formacie, co zwiększa bezpieczeństwo oraz niezawodność aplikacji.

Przykładowe DTO dla użytkownika może wyglądać tak:

				
					export class StworzUzytkownikDto {
  imie: string;
  wiek: number;
}

				
			

Walidację danych możemy zaimplementować za pomocą dodatkowych dekoratorów, takich jak te oferowane przez bibliotekę class-validator. W tym celu instalujemy niezbędne biblioteki:

				
					npm install class-validator class-transformer

				
			

A następnie modyfikujemy nasze DTO:

				
					import { IsString, IsInt, Min, Max } from 'class-validator';

export class StworzUzytkownikDto {
  @IsString()
  imie: string;

  @IsInt()
  @Min(1)
  @Max(100)
  wiek: number;
}

				
			

W powyższym przykładzie używamy dekoratorów @IsString(), @IsInt(), @Min() oraz @Max() do walidacji danych. Dzięki temu mamy pewność, że użytkownik podaje dane w odpowiednim formacie, co znacznie ułatwia dalsze przetwarzanie.

Podsumowanie

NestJS to potężny framework, który umożliwia tworzenie modularnych i skalowalnych aplikacji serwerowych. W tym wpisie omówiliśmy podstawy organizacji aplikacji NestJS za pomocą modułów, kontrolerów i serwisów. Zrozumieliśmy, jak działają kontrolery, jaką pełnią rolę serwisy, oraz jak wprowadzać logikę biznesową w serwisach. Przyjrzeliśmy się także zarządzaniu zapytaniami HTTP, parametrami tras i zapytań, a także wprowadzaniu walidacji danych wejściowych za pomocą DTO.

Modularność NestJS, w połączeniu z TypeScript i solidnym zarządzaniem trasami HTTP, czyni ten framework doskonałym wyborem do budowania aplikacji serwerowych, które są nie tylko skalowalne, ale także dobrze zorganizowane. Jeśli chcesz zbudować aplikację, która będzie łatwa do utrzymania, NestJS to doskonały wybór!