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:
- Nest.js został stworzony, aby pomóc programistom w tworzeniu zarówno monolitycznych aplikacji, jak i mikrousług.
- Chociaż jest potężny, jest również przyjazny dla programistów; łatwy w użyciu, szybki do nauczenia się i łatwy w zastosowaniu.
- 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.
- Posiada interfejs wiersza poleceń, który pomaga zwiększyć produktywność programistów i łatwość programowania.
- 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.
- 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.
- 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.
- Nest.js integruje się z Jest, co ułatwia pisanie testów jednostkowych aplikacji.
- 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:
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:
- Moduły
- Sterowniki
- 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:
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/common
nastę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.ts
i 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:
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:
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ą.
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!
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:
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:
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.
- Pomaga w testach jednostkowych.
- W przypadku wstrzykiwania zależności, kod wzorcowy jest redukowany, ponieważ inicjalizacja zależności jest wykonywana przez komponent iniektora.
- Proces rozszerzania aplikacji staje się prostszy.
- 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 implementujePipeTransform
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:
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:
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:
Ale w przypadku wartości liczbowej sprawdzanie poprawności przebiega pomyślnie:
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!