Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Przewodnik po Nest.js – budowanie REST API z wykorzystaniem Nest i Node

Jako programista zaplecza Node.js zgodzisz się, że domyślnie Node.js jest bardzo prosty i nie przyjmuje żadnych założeń dotyczących tego, czego potrzebujesz podczas tworzenia aplikacji. W rezultacie odpowiadasz za konfigurację wszystkiego, czego chcesz używać w aplikacji, w tym za obsługę routingu, wykonywanie wywołań API, konfigurowanie języka TypeScript lub Web Sockets, a nawet za podstawowe rzeczy, takie jak organizacja kodu, struktura plików i konwencje nazewnictwa .

Zarządzanie aplikacją na dużą skalę może być trudnym zadaniem, zwłaszcza jeśli nie została zaprojektowana z przejrzystą strukturą i ścisłymi wytycznymi dotyczącymi organizacji kodu.

Nest.js próbuje rozwiązać niektóre z tych problemów, tworząc abstrakcję wokół Node.js, dzięki czemu jako programista możesz skupić się na problemie z aplikacją, a nie na innych drobnych szczegółach implementacji.

W tym przewodniku poznasz podstawowe podstawy Nest.js od góry do dołu, aby przyspieszyć tworzenie aplikacji Node.js klasy korporacyjnej przy pomocy Nest.js w mgnieniu oka.

Wszystko, czego nauczymy się z tego przewodnika, będzie stopniowe; obejmujące wiele podstaw pojęć wprowadzających. Aby w pełni wykorzystać ten przewodnik, pomocne jest kodowanie.

Zanurzmy się od razu, ludzie!

Kod źródłowy: Jak zwykle możesz forkować i majstrować przy kodzie źródłowym hostowanym GitHub.

Uwaga: Będziemy używać Postmana do testowania interfejsu API w naszej wersji demonstracyjnej. Można go pobrać na Strona pobierania listonosza. Alternatywnie możesz po prostu użyć przeglądarki, wiersza poleceń curl narzędzie lub jakiekolwiek inne narzędzie, które możesz znać.

Czym jest plik Nest.js

Pomyśl o Nest.js jako o nadzbiorze Node.js, który usuwa trudne zadania, narzędzia i szablonowy kod, a jednocześnie dodaje pełnoprawny zestaw narzędzi do tworzenia aplikacji przy użyciu nowoczesnego JavaScript i TypeScript.

Nest.js zapewnia gotową architekturę aplikacji, która pozwala programistom i zespołom tworzyć wysoce skalowalne, testowalne, luźno powiązane i łatwe w utrzymaniu, wykorzystując łatwo dostępne i wyróżniające się opcje i moduły w społeczności, takie jak te dostępne w Aplikacje Express.js. Możesz nawet zamienić Express (którego domyślnie używa pod maską) na Fastify, ale oznaczałoby to, że być może będziesz musiał użyć różnych bibliotek zgodnych z Fastify w swojej aplikacji.

Łączy w sobie cechy programowania funkcjonalnego, programowania obiektowego i funkcjonalnego programowania reaktywnego, a także zawiera ponad 52.4 tys. gwiazdek i 6.2 tys. GitHub i tygodniową liczbą pobrań do 1,784,004 XNUMX XNUMX, progresywna platforma Node.js jest popularnym narzędziem do tworzenia wydajnych, skalowalnych aplikacji serwerowych klasy korporacyjnej.

Funkcje Nest.js

Oto powody, dla których Nest.js stał się tak popularnym frameworkiem Node.js:

  1. Nest.js został stworzony, aby pomóc programistom w tworzeniu zarówno monolitycznych aplikacji, jak i mikrousług.
  2. Chociaż jest potężny, jest również przyjazny dla programistów; łatwy w użyciu, szybki do nauczenia się i łatwy w zastosowaniu.
  3. Wykorzystuje TypeScript (nadzbiór JavaScript) po wyjęciu z pudełka i zapewnia programistom miejsce na pisanie łatwego w utrzymaniu kodu wolnego od błędów czasu wykonywania.
  4. Posiada interfejs wiersza poleceń, który pomaga zwiększyć produktywność programistów i łatwość programowania.
  5. Podczas budowania za pomocą Nest.js procesy programistyczne są usprawniane i oszczędza się czas, niezależnie od tego, czy uruchamiasz minimalny produkt, czy pracujesz nad aplikacją, ponieważ Nest ma domyślnie niesamowitą strukturę folderów projektu.
  6. Obsługuje wiele modułów specyficznych dla Nest, które pomagają w integracji typowych koncepcji i technologii, w tym TypeORM, GraphQL, rejestrowania, sprawdzania poprawności, Mongoose, WebSockets, buforowania itp.
  7. Nest.js może pochwalić się posiadaniem jednej z najlepszych dokumentacji dla dowolnego frameworka. Jego dokumentacja jest dokładna, łatwa do zrozumienia i pomocna w oszczędzaniu czasu debugowania, ponieważ przychodzi bez wysiłku, gdy istnieje potrzeba rozwiązania problemu.
  8. Nest.js integruje się z Jest, co ułatwia pisanie testów jednostkowych aplikacji.
  9. Jest zbudowany zarówno dla małych, jak i dużych aplikacji korporacyjnych.

Tworzenie projektu Nest.js

Aby rozpocząć korzystanie z Nest.js na komputerze lokalnym, musisz najpierw zainstalować interfejs wiersza poleceń (CLI) Nest, który pomógłby utworzyć rusztowanie dla nowego folderu projektu Nest.js i zapełnić go podstawowymi plikami i modułami potrzebnymi do Aplikacja Nest.js.

Uruchom następujące polecenie, aby zainstalować interfejs wiersza poleceń Nest.js:

$ npm i -g @nestjs/cli
// Or
$ yarn global add @nestjs/cli
// Or
$ pnpm add -g @nestjs/cli

Po pomyślnym zainstalowaniu Nest.js CLI globalnie na komputerze lokalnym możesz uruchomić nest w wierszu poleceń, aby zobaczyć różne polecenia, z których możemy skorzystać:

$ nest

Prowadzi do:

Usage: nest  [options]

Options:
  -v, --version                                   Output the current version.
  -h, --help                                      Output usage information.

Commands:
  new|n [options] [name]                          Generate Nest application.
  build [options] [app]                           Build Nest application.
  start [options] [app]                           Run Nest application.
  info|i                                          Display Nest project details.
  add [options]                          Adds support for an external library to your project.
  generate|g [options]  [name] [path]  Generate a Nest element.
    Schematics available on @nestjs/schematics collection:
      ┌───────────────┬─────────────┬──────────────────────────────────────────────┐
      │ name          │ alias       │ description                                  │
      │ application   │ application │ Generate a new application workspace         │
      │ class         │ cl          │ Generate a new class                         │
      │ configuration │ config      │ Generate a CLI configuration file            │
      │ controller    │ co          │ Generate a controller declaration            │
      │ decorator     │ d           │ Generate a custom decorator                  │
      │ filter        │ f           │ Generate a filter declaration                │
      │ gateway       │ ga          │ Generate a gateway declaration               │
      │ guard         │ gu          │ Generate a guard declaration                 │
      │ interceptor   │ itc         │ Generate an interceptor declaration          │
      │ interface     │ itf         │ Generate an interface                        │
      │ middleware    │ mi          │ Generate a middleware declaration            │
      │ module        │ mo          │ Generate a module declaration                │
      │ pipe          │ pi          │ Generate a pipe declaration                  │
      │ provider      │ pr          │ Generate a provider declaration              │
      │ resolver      │ r           │ Generate a GraphQL resolver declaration      │
      │ service       │ s           │ Generate a service declaration               │
      │ library       │ lib         │ Generate a new library within a monorepo     │
      │ sub-app       │ app         │ Generate a new application within a monorepo │
      │ resource      │ res         │ Generate a new CRUD resource                 │
      └───────────────┴─────────────┴──────────────────────────────────────────────┘

Tutaj pokazano, jak korzystać z poleceń, i możesz teraz dotknąć new|n [options] [name] polecenie, aby utworzyć swój pierwszy projekt Nest.js:

$ nest new getting-started-with-nestjs
// Or
$ nest n getting-started-with-nestjs

Następnie zostaniesz zapytany, jakiego menedżera pakietów chcesz użyć:

? Which package manager would you ❤️ to use? (Use arrow keys)
  npm
  yarn
> pnpm

Zapraszam do wyboru menedżera pakietów do wyboru, pójdę z nim pnpm. Dzieje się tak, ponieważ jest około trzy razy wydajniejszy i szybszy niż NPM, a dzięki szybkiemu systemowi pamięci podręcznej PNPM jest również szybszy niż Yarn.

Po wybraniu menedżera pakietów proces instalacji jest kontynuowany, po czym zostanie utworzona aplikacja Nest.js.

Teraz możesz cd do nowo utworzonego projektu i otwórz go w wybranym edytorze:

$ cd getting-started-with-nestjs

Po utworzeniu projektu możemy go uruchomić za pomocą jednego z następujących poleceń:

$ npm run start
// Or
$ yarn start
// Or
$ pnpm run start

Jeśli spojrzysz na package.json plik, zauważysz w segmencie script wartość for pnpm run start is nest start:


    
"start": "nest start",

Oznacza to, że możesz także uruchomić aplikację Nest.js, uruchamiając:

$ nest start

Spojrzenie na strukturę projektu Nest.js

Przyjrzyjmy się bliżej strukturze aplikacji Nest:

/package.json

Połączenia package.json plik jest sercem projektu Node.js, a co za tym idzie, projektu Nest.js. Przechowuje wszystkie metadane dotyczące projektu i definiuje różne właściwości funkcjonalne projektu, które są potrzebne do zainstalowania zależności aplikacji lub uruchomienia skryptów projektu.

Widzieliśmy już zdolność start skrypt.

Połączenia start:dev profile umożliwia obserwowanie zmian w aplikacji i automatyczne jej ponowne ładowanie, bez konieczności zatrzymywania aplikacji i jej ponownego uruchamiania – i służy do rozwoju. The start:prod skrypt jest przydatny, gdy chcesz przetestować, czy Twoja aplikacja jest gotowa do produkcji, a także podczas wdrażania jej do produkcji, wraz z innymi skryptami do testowania aplikacji Nest.js.

@nestjs/platform-express definiuje express jako domyślny serwer HTTP w aplikacji Nest.

/tsconfig.json

Połączenia tsconfig.json file to plik napisany w formacie JSON (JavaScript Object Notation), który definiuje opcje związane z TypeScript wymagane do skompilowania aplikacji Nest.

/nest-cli.json

Zawiera metadane potrzebne do tworzenia, organizowania lub wdrażania aplikacji Nest.

/test

Ten katalog zawiera wszystkie pliki potrzebne do uruchomienia testów Nest. Nest używa frameworka Jest do testowania z konfiguracją Jest w jest-e2e.json plik.

/src

Połączenia src katalog jest folderem nadrzędnym dla rdzenia projektu Nest. Posiada main.ts plik, który jest plikiem, w którym uruchamiana jest aplikacja Nest. Praca main.ts plik ma się załadować AppModule z którego jest importowany /src/app.module.ts.

W dalszej części tego przewodnika dowiemy się o modułach; jeden z głównych komponentów aplikacji Nest.js.

Połączenia AppModule jest klasą utworzoną jako moduł przy użyciu @Module dekorator. w app.module.ts file, AppService od ./app.service i AppController od ./app.controller są również importowane.

Połączenia AppController jest również klasą utworzoną przy użyciu @Controller dekorator, podczas gdy AppService jest klasą utworzoną przy użyciu @Injectable adnotacja.

Fajną rzeczą w Nest jest to, że ma bardzo niewiele dekoratorów, które dodają metadane do dowolnej klasy, a metadane definiują cel tej klasy, tak że:

  • @Controller()przekształca klasę w kontroler.
  • @Module() przekształca klasę w moduł.
  • @Injectable() przekształca klasę w dostawcę.

Również w src katalog to app.controller.spec.ts plik, który jest plikiem testowym dla Kontrolerów.

Możemy uruchomić aplikację za pomocą nest start.

Uruchomienie aplikacji o godz http://localhost:3000 w Twojej przeglądarce:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Możemy zmienić zawartość wyświetlaną pod adresem http://localhost:3000, kierując się do app.service.ts plik, w którym zdefiniowano dostawcę trasy indeksu.

Elementy składowe aplikacji Nest.js

Istnieją trzy główne komponenty aplikacji Nest.js:

  1. Moduły
  2. Sterowniki
  3. Dostawcy

Poznając elementy składowe aplikacji Nest, najpierw wyczyśćmy projekt Nest, usuwając plik app.controller.spec.ts, ./app.service, app.module.ts, ./app.controller akta; wyjeżdżając właśnie main.ts, aby naśladować cykl życia programowania od zera.

W tym momencie, kiedy usuniemy import AppModule plik z main.ts, pojawia się monit, że nie podano argumentu dla „modułu”.

Aby zademonstrować elementy składowe aplikacji Nest, przyjrzymy się prostej implementacji profilu użytkownika, budując interfejs API REST do obsługi operacji CRUD na obiekcie.

Moduły

W src folder utwórz nowy app.module.ts plik, a następnie utwórz plik AppModule class, którą eksportujemy.

Następnie zaimportuj AppModule klasa w main.ts, i biegnij nest start.

Nawigować do http://localhost:3000 w przeglądarce i pojawi się błąd 404:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Dzieje się tak, ponieważ nie zdefiniowaliśmy jeszcze trasy dla podstawowego adresu URL aplikacji Nest.

Powrót w app.module.ts, mamy AppModule Klasa, którą mamy, nie jest jeszcze modułem Nest. Aby uczynić go modułem Nest, dodajemy plik @Module() dekorator, z którego jest importowany @nestjs/commonnastępnie mijamy pusty obiekt.



import { Module } from '@nestjs/common';
@Module({})

export class AppModule {}

Teraz mamy moduł Nest.js!

Uwaga: Moduł to klasa z adnotacją a @Module() dekorator.

Każda aplikacja Nest ma moduł główny, który służy jako punkt wejścia do rozwiązania struktury i relacji aplikacji Nest.

Zdecydowanie zaleca się stosowanie wielu modułów do organizowania komponentów aplikacji.

Połączenia @Module() decorator umożliwia programistom definiowanie metadanych dotyczących klasy w aplikacji Nest.

W przypadku, gdy istnieje wiele modułów, takich jak moduł użytkowników, moduł zamówień, moduł czatu itp app.module.ts należy użyć do rejestracji wszystkich pozostałych modułów aplikacji Nest.

Tworzenie tras; Kontrolery

Do tworzenia tras w aplikacjach Nest potrzebne są kontrolery. Celem administratora jest otrzymywanie określonych żądań dotyczących aplikacji Nest; kontrolowanie cyklu zapytań i odpowiedzi dla różnych tras w aplikacji.

Gdy żądanie HTTP jest przesyłane od klienta do aplikacji Nest, trasa zgodna z trasą, w której wysyłane jest żądanie, obsługuje żądanie i zwraca odpowiednią odpowiedź.

Aby utworzyć kontroler w aplikacji Nest, musimy skorzystać z @Controller() dekorator.

W src katalog, utwórz nowy plik app.contoller.tsi tam możemy zdefiniować kontroler Nest:

import { Controller } from '@nestjs/common';

@Controller({})

export class AppController {}

To jest to! Mamy bardzo fajny kontroler, ale aby utworzyć nową trasę, musimy najpierw powiadomić naszą aplikację Nest o utworzonym kontrolerze.

Aby to osiągnąć, importujemy AppController w app.module.ts i zdefiniuj informacje o kontrolerach w @Module() dekorator – jako tablica kontrolerów:



import { Module } from '@nestjs/common';
import { AppController } from './app.controller';

@Module({
  controllers: [AppController],
})

export class AppModule {}

Obsługa żądań GET

Następnie definiujemy proste getUser() trasa (z @Get() dekorator używany do obsługi żądań HTTP GET do określonej ścieżki), aby służył jako trasa bazowa, możemy uzyskać do niego dostęp w przeglądarce pod adresem https://localhost:3000:



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

@Controller({})

export class AppController {
  @Get()
  getUser() {
    return 'I am a great person';
  }
}

To skutkuje:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Hmm, tutaj zwracamy tylko ciąg znaków, ale co by było, gdybyśmy chcieli zwrócić obiekt? Zamiast stringa możemy zdefiniować obiekt:



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

@Controller({})

export class AppController {
  @Get()
  getUser() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
}

Nawigować do http://localhost:3000 w przeglądarce, a zobaczysz obiekt:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Z dala od trasy podstawowej, co powiesz na utworzenie trasy podobnej do http://localhost:3000/user do pobierania wszystkich użytkowników?

Kontroler do obsługi takiej trasy możemy stworzyć na kilka sposobów.

Jednym ze sposobów byłoby zdefiniowanie nowej metody, używając metody @Get() dekorator/opiekun.

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

@Controller({})

export class AppController {
  @Get()
  getUser() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
}

Nest.js zapewnia dekoratory lub moduły obsługi dla wszystkich różnych metod HTTP, w tym @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), @Head().

Połączenia @All() decorator definiuje punkt końcowy, który obsługuje wszystkie różne metody.

Obsługa żądań POST

Możemy również zdefiniować żądania POST do przechowywania danych w bazie danych, korzystając z @Post() dekorator:

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

@Controller({})
export class AppController {
  @Post()
  store() {
    return 'Post request successful';
  }
}

Następnie testujemy żądanie POST za pomocą Postmana i zauważamy, że łańcuch został pomyślnie zwrócony zgodnie z definicją.

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Możesz zapytać, co jeśli chcę zrobić coś więcej niż tylko zwrócić dane? Być może, aby przesłać dane.

W tym celu musisz wstrzyknąć dane do metody trasy, jak pokazano:

import { Controller, Post, Req } from '@nestjs/common';
import { Request } from 'express';

@Controller({})
export class AppController {
  @Post()
  store(@Req() req: Request) {
    return req.body;
  }
}

Teraz, kiedy testujemy żądanie POST z Postmanem, jesteśmy w stanie zobaczyć wysyłane dane. W tym przypadku jest to po prostu pusty obiekt:

Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Routing dynamiczny z parametrami trasy

Załóżmy, że chcesz zaakceptować dane dynamiczne jako część żądania. Najpierw musimy zdefiniować token w ścieżce trasy, aby zanotować dynamiczną pozycję na trasie/adresie URL, następnie używając @Param() dekorator, dostęp do parametru trasy można uzyskać w następujący sposób:

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

@Controller({})
export class AppController {
  @Get('/:userId')
  getUser(@Param() userId: number) {
    return userId;
  }
}

Połączenia userId jest zwracany pomyślnie:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Obsługa żądań asynchronicznych

Nest.js jest w stanie obsłużyć żądania asynchroniczne, które zwracają obietnicę przy użyciu różnych podejść:

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

@Controller({})
export class AppController {
  @Get()
  async findAll(): Promise {
    return [];
  }
}

W powyższym podejściu asynchroniczność jest obsługiwana za pomocą metody async słowo kluczowe. Innym podejściem jest zwrócenie obserwowalnych strumieni RxJS:

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

@Controller({})
export class AppController {
  @Get()
  findAll(): Observable {
    return of([]);
  }
}

Tutaj Nest.js zasubskrybuje źródło pod maską, a po zakończeniu strumienia automatycznie pobierze ostatnią wyemitowaną wartość.

Obsługa przekierowań w Nest

Połączenia @Redirect() decorator służy do przekierowania odpowiedzi na inny adres URL. The @Redirect() dekorator akceptuje dwa argumenty — adres URL, na który ma zostać przekierowane, oraz kod statusu po przekierowaniu, z których oba są opcjonalne:

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

@Controller({})
export class AppController {
  @Get()
  @Redirect('https://www.ucheazubuko.com', 302)
  getSite() {
    return { url: 'https://stackabuse.com' };
  }
}

Zwracany kod stanu

Aby zwrócić kod stanu dla dowolnego żądania obsługiwanego na serwerze Nest.js, plik @HttpCode(…) łatwo przechodzi.

W Nest domyślny kod stanu dla żądań GET to 200, żądanie POST to 201, żądanie błędu to 304

Kod stanu dla żądania serwera można zdefiniować w sposób pokazany poniżej:

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

@Controller({})
export class AppController {
  @Post()
  @HttpCode(204)
  create() {
    return 'This action adds a new user to the app.';
  }
}

Obsługa żądań USUŃ

Podobnie jak w przypadku żądania POST, żądanie usunięcia można obsłużyć w następujący sposób:

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

@Controller({})
export class AppController {
  @Delete('/:userId')
  delete(@Param() params: { userId: number }) {
    return params;
  }
}

Obsługa żądań UPDATE

Żądanie aktualizacji określonych danych na serwerze może być obsłużone za pomocą @Patch() dekorator:

import { Controller, Patch, Req} from '@nestjs/common';
import { Request } from 'express';

@Controller({})
export class AppController {
  @Patch('/:userId')
  update(@Req() req: Request) {
    return req.body;
  }
}

Teraz, gdy widzieliśmy różne sposoby definiowania typowych kontrolerów, które często mielibyśmy na niezawodnym serwerze, ważne jest, aby pamiętać, że kontroler powinien być oszczędny, czysty i zdefiniowany dla każdego przypadku użycia, tak aby jeśli istnieje inny kontroler do zdefiniowania user tras, należy utworzyć osobny katalog i poświęcić go do obsługi – z dala od AppController.

Następnie w user.controller.ts, możemy skonfigurować wszystkie procedury obsługi tras, aby były poprzedzone prefiksem /user/ pisząc kod, jak pokazano poniżej:



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

@Controller('/user')
export class UserController {
  @Get()
  getUser() {
    return 'I am from the user controller';
  }
}

Następnie zarejestruj się UserController w tablicach kontrolerów w app.modules.ts:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { UserController } from './controllers/user/user.controller';

@Module({
  controllers: [AppController, UserController],
})

export class AppModule {}

Kiedy nawigujemy do https:localhost:3000/user, zwraca pomyślnie:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Aby folder projektu był jeszcze bardziej uporządkowany niż obecnie, możemy zdefiniować plik user.module.ts plik, w którym zdefiniujemy plik UserController:

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';

@Module({
  controllers: [UserController],
})

export class UserModule {}

Następnie zaimportuj UserModule najnowszych app.module.ts:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { UserModule } from './user/user.module';

@Module({
  controllers: [AppController],
  imports: [UserModule],
})

export class AppModule {}

Dzięki temu będziemy mogli uzyskać taki sam efekt jak poprzednio.

Uwaga: Nest ułatwia (g)generowanie (modu)duli i (ko)ntrollerów za pomocą nest g mo i nest g co polecenia. Konkretne moduły, np user moduł i kontrolery można również szybko utworzyć za pomocą Nest CLI, uruchamiając polecenia: nest g mo user – aby utworzyć moduł użytkownika, oraz nest g co user – aby utworzyć kontroler użytkownika.

Dostawcy

Całe pobieranie danych z bazy danych powinno być obsługiwane przez dostawców, a nie przez kontrolerów, aby stworzyć warstwę abstrakcji między kodem skierowanym do użytkownika a kodem, który wchodzi w interakcje z potencjalnie wrażliwymi danymi. Pomiędzy tymi warstwami można ustawić walidację, aby zapewnić prawidłową obsługę bazy danych. Dzięki Nest CLI możemy tworzyć dostawców, generując usługi:

$ nest g s user

To tworzy UserService w którym zdefiniowalibyśmy całą logikę biznesową dla UserController, Tak aby UserController obsługuje tylko żądania i odpowiedzi. W user.service.ts, widzimy, że @Injectable() Dekorator służy do definiowania klasy. W Nest użycie @Injectable() decorator ma na celu przekształcenie usług, repozytoriów lub klasy pomocników w dostawcę.

Dostawcy są wstrzykiwani do klasy przez jej konstruktora. Przyjrzyjmy się bliżej przykładowi.

Wcześniej, w user.controller.ts, zdefiniowaliśmy logikę biznesową pobierania obiektu użytkownika, ale teraz powinniśmy zdefiniować to samo w pliku UserService:



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

@Controller({})

export class AppController {
  @Injectable()
  get() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria'; };
  }
}

Następnie w user.controller.ts plik, zdefiniujmy konstruktor w pliku UserController klasa. W tym konstruktorze zapewniamy private userService, który jest rodzajem UserService klasa. Dzięki temu prywatnemu jesteśmy w stanie wykorzystać logikę biznesową, którą zdefiniowaliśmy wcześniej w celu pobierania użytkowników:



import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('/user')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUser() {
    return this.userService.get();
  }
}

W ten sposób, UserController klasa, teraz zależy od UserService klasa w koncepcji znanej jako
zastrzyk zależności.

W ten sam sposób, logika w obu user.controller.ts i user.service.ts pliki są odpowiednio aktualizowane:



import {
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
  Req,
} from '@nestjs/common';
import { Request } from 'express';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUsers() {
    return this.userService.get();
  }
  @Get('/:userId')
  getUser(@Param() param: { userId: number }) {
    return this.userService.getUser(param);
  }
  @Post()
  store(@Req() req: Request) {
    return this.userService.create(req);
  }
  @Patch('/:userId')
  update(@Req() req: Request, @Param() param: { userId: number }) {
    return this.userService.update(req, param);
  }
  @Delete()
  delete(@Param() param: { userId: number }) {
    return this.userService.delete(param);
  }
}


import { Injectable } from '@nestjs/common';
import { Request } from 'express';

@Injectable()
export class UserService {
  get() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
  getUser(param: { userId: number }) {
    return param;
  }
  create(req: Request) {
    return req.body;
  }
  update(req: Request, param: { userId: number }) {
    return { body: req.body, param };
  }
  delete(param: { userId: number }) {
    return param;
  }
}

Teraz sprawdźmy, czy punkty końcowe działają tak, jak powinny, używając Postmana.

Demistyfikujące wstrzykiwanie zależności w Nest.js

Tworząc mniejsze komponenty aplikacji, takie jak klasa lub moduł, Twoja klasa może zależeć od funkcjonalności innej klasy lub modułu, na przykład od potrzeby skorzystania z usługi HTTP zapewnianej przez inną klasę w celu wywołania interfejsu API lub warstwy usług, które współdziałają z warstwą trwałości.

Zależności można dostarczać w kontrolerach za pośrednictwem wstrzykiwanie zależności.

Wstrzykiwanie zależności to koncepcja i wzorzec programowania, który wyraża sposób, w jaki części aplikacji są dostarczane do innych części aplikacji, które ich wymagają, w taki sposób, aby zapewnić wysoką spójność, ale luźne powiązanie.

Nest obsługuje wstrzykiwanie zależności i możesz go używać w swoich aplikacjach Nest, aby zwiększyć modułowość swojego projektu.

Praktyczna ilustracja jest przedstawiona w następujący sposób:

Załóżmy, że klasa A wykorzystuje pewną funkcjonalność klasy B. Wtedy mówi się, że klasa A zależy od klasy B. Tak więc, aby użyć klasy B w klasie A, musimy najpierw utworzyć instancję klasy B (tzn. Obiekt klasy B): const b = new B ().
Przeniesienie zadania utworzenia instancji klasy do innej klasy i bezpośrednie użycie zależności w zapewnianej klasie (komponent wtryskiwacza) jest znane jako wstrzyknięcie zależności.

Rada: Wstrzykiwanie zależności, czyli DI, to jedno z podstawowych pojęć w frameworkach takich jak Spring Boot, Nest.js i Angular.js, jeśli chcesz przeczytać więcej na ten temat, możesz sprawdzić oficjalna dokumentacja Angulara.

Zazwyczaj klasa powinna koncentrować się wyłącznie na wypełnianiu swoich funkcji, a nie na tworzeniu różnych obiektów, których może wymagać lub nie.

Korzyści z wstrzykiwania zależności.

  1. Pomaga w testach jednostkowych.
  2. W przypadku wstrzykiwania zależności, kod wzorcowy jest redukowany, ponieważ inicjalizacja zależności jest wykonywana przez komponent iniektora.
  3. Proces rozszerzania aplikacji staje się prostszy.
  4. Wstrzykiwanie zależności pomaga umożliwić luźne sprzężenie.

Eksplorowanie ładunków żądań

Pamiętaj, że w różnych procedurach obsługi żądań, takich jak POST i PATCH, byliśmy w stanie wykorzystać żądanie wysyłane przez serwer za pomocą @Req() dekorator. Jest jednak coś więcej.

Zamiast pobierać cały obiekt żądania, możemy po prostu wykorzystać określone części obiektu żądania, których potrzebujemy.
W ten sposób Nest udostępnia różne dekoratory, których można używać z modułami obsługi tras HTTP w celu uzyskania dostępu do obiektów Express of Fastify:

Dekoratorzy gniazd Fastify lub Express obiekt, do którego uzyskuje się dostęp
`@Żądanie(), @Żądanie()` `wymaganie`
`@Odpowiedź(), @Res()` `re` s`
`@Następny()` `następny`
`@Sesja()` `żądanie.sesja`
`@Param(param?: ciąg znaków)` `wymagane.parametry` / `wymagane.parametry[parametry]`
`@Body(parametr?: string)` `wymagana treść` / `wymagana treść[parametr]`
`@Zapytanie(parametr?: ciąg znaków)` `wymagane.zapytanie` / `wymagane.zapytanie[parametr]`
`@Nagłówki(parametr?: ciąg znaków)` `wymagane nagłówki` / `wymagane nagłówki[parametr]`
`@Ip()` `wymagany adres IP`
`@ParamHost()` `wymagane hosty`

Typowym przykładem byłoby zastąpienie @Req() dekorator, którego użyliśmy wcześniej, aby uzyskać dostęp do ciała wyniku, z @Body() co może już dać nam bezpośredni dostęp do treści żądania bez wiercenia:



@Post()
store(@Body() body: any) {
  return this.userService.create(body);
}

@Patch('/:userId')
update(@Body() body: any, @Param() param: { userId: number }) {
  return this.userService.update(body, param);
}


create(body: any) {
  return body;
}

update(body: any, param: { userId: number }) {
  return { body: body, param };
}

W niektórych przypadkach możesz chcieć pobrać tylko określone właściwości ładunku żądania. W takim przypadku konieczne byłoby zdefiniowanie schematu obiektu przesyłania danych (DTO). Schemat przesyłania danych to obiekt, który definiuje kopię pobieranego obiektu, ale służy przede wszystkim do przesyłania danych między obiektem, który ma zostać zapisany lub pobrany, a warstwą trwałości. Zazwyczaj, ponieważ ten proces jest bardziej podatny na ataki – DTO nie zawiera tak wielu wrażliwych punktów danych. Ta cecha pozwala również na pobieranie tylko niektórych pól obiektu.

W Nest zaleca się użycie klas do zdefiniowania obiektu transferu danych, ponieważ wartość klas jest zachowywana podczas kompilacji.

Załóżmy, że treść żądania zawiera token, a Ty nie chcesz pobierać ani aktualizować takich danych, wtedy DTO można zdefiniować w sposób pokazany poniżej:



@Patch('/:userId')
update(
  @Body() updateUserDto: { name: string; email: string },
  @Param() param: { userId: number },
) {
  return this.userService.update(updateUserDto, param);
}


update(
  updateUserDto: { name: string; email: string },
  param: { userId: number },
) {
  return { body: updateUserDto, param };
}

Można jednak zauważyć, że zdefiniowaliśmy typ dla updateUserDto dwa razy; W user.service.ts oraz w user.controller.ts, ale musimy zachować nasze kody DRY (nie powtarzaj się), abyśmy nie powtarzali się wokół bazy kodu.

W tym celu w nowym folderze /user/dto /user katalog, musimy utworzyć plik /update-user.dto.ts z .dto.ts rozszerzenie, w którym definiujemy i eksportujemy plik UpdateUserDto klasa do użytku w user.service.ts i user.controller.ts plików:



export class UpdateUserDto {
  name: string;
  email: string;
}

...
import { UpdateUserDto } from './dto/update-user.dto';

@Patch('/:userId')
update(
  @Body() updateUserDto: UpdateUserDto,
  @Param() param: { userId: number },
) {
  return this.userService.update(updateUserDto, param);
}

...
import { UpdateUserDto } from './dto/update-user.dto';

update(updateUserDto: UpdateUserDto, param: { userId: number }) {
  return { body: updateUserDto, param };
}

Potok i walidacja

Załóżmy, że istnieje potrzeba sprawdzenia poprawności danych uzyskanych po wysłaniu żądania przez serwer.

W Nest możemy przetestować poprawność wszelkich danych wchodzących lub wychodzących z aplikacji za pomocą potoków instalujących dwie zależności – class-validator i class-transformer.

Potok jest klasą zdefiniowaną za pomocą @Injectable() dekorator (a więc potoki są dostawcami), który implementuje PipeTransform interfejs. Przekształcają dane do pożądanego formatu i oceniają dane w taki sposób, że jeśli dane okażą się prawidłowe, przechodzą bez zmian, w przeciwnym razie zgłaszany jest wyjątek. Aby użyć potoku, musisz powiązać instancję określonej klasy potoku z odpowiednim kontekstem.

Połączenia class-validator package umożliwia walidację dekoratorów i nie-dekoratorów przy użyciu walidator.js wewnętrznie. Podczas class-transformer package umożliwia transformację obiektów w instancję klasy, transformację klasy w obiekt oraz serializację lub deserializację obiektów w oparciu o określone kryteria.

Osiem rur dostarczonych przez Nest to:

  • ValidationPipe
  • ParseArrayPipe
  • ParseIntPipe
  • ParseUUIDPipe
  • ParseBoolPipe
  • DefaultValuePipe
  • ParseEnumPipe
  • ParseFloatPipe

Aby zademonstrować sprawdzanie poprawności w Nest w tym przewodniku, użyjemy wbudowanego ValidationPipe który umożliwia wymuszanie sprawdzania poprawności ładunków żądań i dobrze łączy się z class-validator pakiet; określone reguły są deklarowane za pomocą prostych adnotacji w deklaracjach Data Transfer Object/lokalnych klas w każdym module.

Aby rozpocząć korzystanie z wbudowanego ValidationPipe z którego jest eksportowany @nestjs/common, zainstalujmy class-validator i class-transformer pakiety:

$ npm i --save class-validator class-transformer
# Or
$ yarn add class-validator class-transformer
# Or
$ pnpm install class-validator class-transformer

Następnie przejdź do main.ts gdzie będziemy wiązać ValidationPipe na poziomie głównym aplikacji, aby zapewnić, że wszystkie punkty końcowe w naszej aplikacji są chronione przed pobieraniem nieprawidłowych danych:



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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}

bootstrap();

Następnie w deklaracjach Data Transfer Object każdego modułu dodajemy kilka reguł walidacji, deklarując odpowiednie kontrole danych dla poszczególnych danych. W naszym przypadku zadeklarowalibyśmy odpowiednie reguły walidacji dla name i email in UpdateUserDto:



import { IsEmail, IsString } from 'class-validator';

export class UpdateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;
}

Połączenia @IsString() dekorator sprawdza, czy podane dane są prawdziwym ciągiem znaków, a @IsEmail() walidator sprawdza, czy podane dane to email, w przeciwnym razie zwraca false i zgłasza wyjątek.

Teraz, jeśli spróbujemy zrobić PATCH żądanie do profilu użytkownika i wprowadź numer zamiast prawidłowego adresu e-mail, na przykład zostanie zgłoszony wyjątek:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Dzięki temu mamy bardzo dobrą weryfikację w naszej aplikacji Nest.

Podczas sprawdzania poprawności za pomocą ValidationPipe, możliwe jest również filtrowanie naszych właściwości, których nie chcemy otrzymywać od naszego modułu obsługi metody. Na przykład, jeśli nasz program obsługi tylko oczekuje name i email właściwości, ale żądanie zawiera również a country własność, możemy usunąć country właściwość z wynikowego obiektu przez ustawienie whitelist do true kiedy tworzymy instancję ValidationPipe:



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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
    }),
  );
  await app.listen(3000);
}

bootstrap();

Wiązanie potoków na poziomie parametrów metody

Można również zdefiniować rury params, również. W tym celu powiążemy potok na poziomie parametrów metody.

Wcześniej, mimo że zdefiniowaliśmy userId być liczbą, zauważysz, że jeśli złożymy wniosek z userId jako ciąg okazuje się skuteczny niezależnie od:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Aby zapewnić, że wartość userId musi zawsze być liczbą, zadeklarujemy, że jest wiążąca getUser() program obsługi metody z kontrolą poprawności, która zapewnia to samo:


...
import { ParseIntPipe } from '@nestjs/common';

@Get('/:userId')
getUser(@Param('userId', ParseIntPipe) userId: number) {
  return this.userService.getUser(userId);
}


getUser(userId: number) {
  return { userId };
}

Połączenia ParseIntPipe definiuje wbudowany ParseInt Pipe i zapewnia, że ​​dane, z którymi jest uruchamiany, muszą być liczbami całkowitymi.

Teraz, kiedy robimy a GET prośba do inwalidy userId ciągu „ab”, sprawdzanie poprawności kończy się niepowodzeniem i zgłaszany jest wyjątek z a 400 Kod statusu:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Ale w przypadku wartości liczbowej sprawdzanie poprawności przebiega pomyślnie:

Przewodnik po Nest.js — Tworzenie interfejsu API REST za pomocą Nest i Node PlatoBlockchain Data Intelligence. Wyszukiwanie pionowe. AI.

Możemy również odpowiednio zaktualizować inne procedury obsługi metod, aby zapewnić odpowiednią walidację:



import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Req,
} from '@nestjs/common';
import { Request } from 'express';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUsers() {
    return this.userService.get();
  }
  @Get('/:userId')
  getUser(@Param('userId', ParseIntPipe) userId: number) {
    return this.userService.getUser(userId);
  }
  @Post()
  store(@Req() req: Request) {
    return this.userService.create(req);
  }
  @Patch('/:userId')
  update(
    @Body() updateUserDto: { name: string; email: string },
    @Param('userId', ParseIntPipe) userId: number,
  ) {
    return this.userService.update(updateUserDto, userId);
  }
  @Delete()
  delete(@Param('userId', ParseIntPipe) userId: number) {
    return this.userService.delete(userId);
  }
}


import { Injectable } from '@nestjs/common';
import { Request } from 'express';
import { UpdateUserDto } from './dto/user-update.dto';

@Injectable()
export class UserService {
  get() {
    return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
  }
  getUser(userId: number) {
    return { userId };
  }
  create(req: Request) {
    return req.body;
  }
  update(updateUserDto: UpdateUserDto, userId: number) {
    return { body: updateUserDto, userId };
  }
  delete(userId: number) {
    return { userId };
  }
}

Teraz zapewniliśmy technikę najlepszych praktyk w zakresie sprawdzania poprawności danych, które trafiają do naszej aplikacji, być może z zewnętrznego źródła, w dowolnym momencie.

Wnioski

W tym przewodniku możesz dowiedzieć się o najnowszym dzieciaku w bloku Node.js; Nest.js i wszystko, co jest potrzebne, aby pomóc Ci rozpocząć, jeśli chcesz zbudować z nim aplikację. Dowiedziałeś się, czym jest Nest, jakie są jego funkcje, jak stworzyć projekt Nest, jak obsługiwać dane przychodzące do aplikacji Nest i jak weryfikować przychodzące dane. W sumie poznałeś elementy składowe dowolnej aplikacji Nest oraz wartość, jaką każdy komponent wnosi do aplikacji Nest.js.

Od tego momentu jest jeszcze wiele do nauczenia się, jeśli chodzi o budowanie aplikacji klasy korporacyjnej za pomocą Nest, ale udało Ci się z powodzeniem omówić podstawowe pojęcia, które mogą sprawić, że będziesz gotowy do wszystkiego, co Cię czeka.

W przyszłości spodziewaj się nowego przewodnika, w którym dowiemy się, jak zbudować spokojny interfejs API za pomocą Nest i MySQL.

Dziękuje za przeczytanie!

Dodatkowe zasoby

Dokumenty Nest.js
Dokumenty kątowe

Znak czasu:

Więcej z Nadużycie stosu