Som en Node.js backend-utvikler vil du være enig i at Node.js som standard er veldig bare bein, og gjør ingen antagelser om hva du trenger mens du bygger en app. Som et resultat er du ansvarlig for å sette opp alt du vil bruke på tvers av en app, inkludert håndtering av ruting, foreta API-anrop, sette opp TypeScript eller Web Sockets, eller til og med grunnleggende ting som kodeorganisering, filstruktur og navnekonvensjoner .
Å administrere en storstilt applikasjon kan være en vanskelig oppgave, spesielt hvis den ikke ble designet med en klar struktur og strenge retningslinjer for kodeorganisering.
Nest.js prøver å takle noen av disse problemene ved å lage en abstraksjon rundt Node.js slik at du som utvikler kan fokusere på applikasjonsproblemet i stedet for andre bittesmå implementeringsdetaljer.
I denne veiledningen vil du lære det grunnleggende om Nest.js fra topp til bunn, med sikte på å få deg oppdatert slik at du kan bygge enterprise-grade Node.js-applikasjoner ved hjelp av Nest.js på kort tid.
Alt vi vil lære gjennom denne veiledningen vil være inkrementell; dekker mye av introduksjonskonsepter. For å få mest mulig ut av denne veiledningen hjelper det å kode med.
La oss dykke rett inn, folkens!
Kildekode: Som vanlig kan du pusle med kildekoden som er vert på GitHub.
OBS: Vi bruker Postman til å teste API-en i demoen vår. Du kan laste den ned på Postman Last ned side. Alternativt kan du ganske enkelt bruke nettleseren, kommandolinjen curl
verktøy, eller et annet verktøy du kanskje er kjent med.
Hva er Nest.js
Tenk på Nest.js som et supersett av Node.js som abstraherer bort vanskelige oppgaver, verktøy og standardkode, samtidig som du legger til et fullverdig verktøysett for applikasjonsutviklingen din ved hjelp av moderne JavaScript og TypeScript.
Nest.js gir en ut-av-boksen applikasjonsarkitektur som lar utviklere og team lage svært skalerbare, testbare, løst koblede og lett vedlikeholdbare, ved å utnytte lett tilgjengelige og fremtredende alternativer og moduler i fellesskapet, som de som er tilgjengelige i Express.js-applikasjoner. Du kan til og med bytte Express (som den bruker under panseret som standard) for Fastify, men å gjøre det vil bety at du kanskje må bruke forskjellige Fastify-kompatible biblioteker i applikasjonen din.
Den kombinerer funksjonene til funksjonell programmering, objektorientert programmering og funksjonell reaktiv programmering, og med mer enn 52.4k stjerner og 6.2k gafler på GitHub og et ukentlig nedlastingstall på opptil 1,784,004 XNUMX XNUMX, er det progressive Node.js-rammeverket et populært valg for å lage effektive, skalerbare og bedriftsbaserte serversideapplikasjoner.
Funksjoner i Nest.js
Følgende er grunner til at Nest.js har vokst til å bli et så populært Node.js-rammeverk:
- Nest.js ble opprettet for å hjelpe utviklere med å bygge både monolittiske applikasjoner og mikrotjenester også.
- Selv om den er kraftig, er den også utviklervennlig å jobbe med; enkel å bruke, rask å lære og enkel å bruke.
- Den utnytter TypeScript (et supersett av JavaScript) ut av esken og gir rom for utviklere til å skrive vedlikeholdbar kode uten kjøretidsfeil.
- Den har et kommandolinjegrensesnitt som bidrar til å øke produktiviteten til utviklere og enkel utvikling.
- Når du bygger med Nest.js, forbedres utviklingsprosessene og det spares tid enten du starter opp et minimum levedyktig produkt eller jobber med en applikasjon fordi Nest kommer med en fantastisk prosjektmappestruktur som standard.
- Den støtter en rekke Nest-spesifikke moduler som hjelper til med integrasjonen av vanlige konsepter og teknologier, inkludert TypeORM, GraphQL, logging, validering, Mongoose, WebSockets, caching, etc.
- Nest.js kan skryte av å ha noe av den beste dokumentasjonen for ethvert rammeverk der ute. Dokumentasjonen er grundig, lett å forstå og nyttig for å spare feilsøkingstid, siden den kommer gjennom uten problemer når det er behov for en løsning på et problem.
- Nest.js integreres med Jest, noe som gjør det enkelt å skrive enhetstester på applikasjonene dine.
- Den er bygget for både små og store bedriftsapplikasjoner.
Opprette et Nest.js-prosjekt
For å komme i gang med Nest.js på din lokale maskin, må du først installere Nest Command Line Interface (CLI), som vil bidra til å stillasere en ny Nest.js-prosjektmappe og fylle ut mappen med kjernefiler og moduler som trengs for en Nest.js-appen.
Kjør følgende kommando for å installere Nest.js kommandolinjegrensesnitt:
$ npm i -g @nestjs/cli
// Or
$ yarn global add @nestjs/cli
// Or
$ pnpm add -g @nestjs/cli
Når du har installert Nest.js CLI globalt på din lokale maskin, kan du kjøre nest
på kommandolinjen for å se ulike kommandoer som vi kan bruke:
$ nest
Resulterer i:
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 │
└───────────────┴─────────────┴──────────────────────────────────────────────┘
Her får du vist hvordan du bruker kommandoene, og kan nå trykke på new|n [options] [name]
kommando for å lage ditt aller første Nest.js-prosjekt:
$ nest new getting-started-with-nestjs
// Or
$ nest n getting-started-with-nestjs
Deretter vil du bli spurt om hvilken pakkebehandling du vil bruke:
? Which package manager would you ❤️ to use? (Use arrow keys)
npm
yarn
> pnpm
Velg gjerne den pakkeforvalteren du ønsker, jeg går med pnpm
. Dette er fordi det er omtrent tre ganger mer effektivt og raskere enn NPM, og med et hurtigbuffersystem er PNPM også raskere enn Yarn.
Etter å ha valgt en pakkebehandling, fortsetter installasjonsprosessen, og Nest.js-appen vil bli opprettet.
Nå kan du cd
inn i det nyopprettede prosjektet, og åpne det med en editor du velger:
$ cd getting-started-with-nestjs
Med prosjektet nå opprettet, kan vi kjøre det med en av følgende kommandoer:
$ npm run start
// Or
$ yarn start
// Or
$ pnpm run start
Hvis du tar en titt på package.json
fil, vil du legge merke til i skriptsegmentet verdien for pnpm run start
is nest start
:
"start": "nest start",
Dette betyr at du også kan kjøre Nest.js-appen ved å kjøre:
$ nest start
En titt på Nest.js-prosjektstrukturen
La oss se nærmere på hvordan en Nest-app er strukturert:
/package.json
De package.json
filen er hjertet av Node.js og ved utvidelse, Nest.js-prosjektet. Den inneholder alle metadata om prosjektet og definerer ulike funksjonelle egenskaper for prosjektet som er nødvendig for å installere applikasjonsavhengigheter eller kjøre prosjektskript.
Vi har allerede sett evnen til start
skript.
De start:dev
profilen gjør det mulig å se etter endringer i applikasjonen og automatisk laste den inn på nytt, uten å måtte stoppe applikasjonen og starte den på nytt – og den er ment for utvikling. De start:prod
skriptet er nyttig når du vil teste om applikasjonen din er produksjonsklar, så vel som når du distribuerer den til produksjon, sammen med andre skript for å teste Nest.js-appen.
@nestjs/platform-express
definerer express som standard HTTP-server i en Nest-applikasjon.
/tsconfig.json
De tsconfig.json
fil er en fil skrevet i JSON (JavaScript Object Notation) som definerer TypeScript-relaterte alternativer som kreves for å kompilere Nest-appen.
/nest-cli.json
Dette inneholder metadata som er nødvendig for å bygge, organisere eller distribuere Nest-applikasjoner.
/test
Denne katalogen inneholder alle filene som trengs for å kjøre Nest-tester. Nest bruker Jest-rammeverket for testing med Jest-konfigurasjon i jest-e2e.json
filen.
/src
De src
katalogen er overordnet mappe for kjernen i Nest-prosjektet. Den holder main.ts
fil som er filen der Nest-appen starter. Jobben til main.ts
filen skal lastes AppModule
som er importert fra /src/app.module.ts
.
Senere i denne veiledningen vil vi lære om moduler; en av hovedkomponentene i en Nest.js-applikasjon.
De AppModule
er en klasse som er opprettet som en modul ved å bruke @Module
dekoratør. I app.module.ts
fil, AppService
fra ./app.service
og AppController
fra ./app.controller
er også importert.
De AppController
er også en klasse som er opprettet ved hjelp av @Controller
dekoratør, mens AppService
er en klasse som er opprettet ved hjelp av @Injectable
merknad.
Det kule med Nest er at det har svært få dekoratører innen som legger til metadata til en hvilken som helst klasse, og at metadata definerer formålet med den klassen, slik at:
@Controller()
forvandler en klasse til en kontroller.@Module()
forvandler en klasse til en modul.@Injectable()
forvandler en klasse til en leverandør.
Også i src
katalogen er app.controller.spec.ts
fil, som er en testfil for kontrollere.
Vi kan kjøre appen ved hjelp av nest start
.
Appen starter kl http://localhost:3000
på nettleseren din:
Vi kan endre innholdet som vises kl http://localhost:3000
, ved å gå over til app.service.ts
fil, der leverandøren for indeksruten ble definert.
Byggesteinene til en Nest.js-app
Det er tre hovedkomponenter i en Nest.js-applikasjon:
- Moduler
- Controllers
- tilbydere
For å lære om byggesteinene til en Nest-app, la oss først rydde opp i Nest-prosjektet ved å slette app.controller.spec.ts
, ./app.service
, app.module.ts
og ./app.controller
filer; forlater bare main.ts
, for å etterligne en utviklingslivssyklus fra bunnen av.
På dette tidspunktet, når vi fjerner den importerte AppModule
fil fra main.ts
, blir vi bedt om at et argument for 'modul' ikke ble gitt.
For å demonstrere byggesteinene til en Nest-app, vil vi ta en titt på en enkel brukerprofilimplementering, ved å bygge et REST API for å håndtere CRUD-operasjoner på et objekt.
Moduler
på src
mappe opprette en ny app.module.ts
fil, og lag deretter en AppModule
klasse, som vi eksporterer.
Deretter importerer du AppModule
klasse inn main.ts
, og løp nest start
.
naviger til http://localhost:3000
i nettleseren din og du får en 404-feil:
Dette er fordi vi ennå ikke har definert en rute for basis-URLen til Nest-appen.
Tilbake i app.module.ts
, vi har AppModule
klasse som vi har er ennå ikke en Nest-modul. For å gjøre det til en Nest-modul legger vi til @Module()
dekoratør som er importert fra @nestjs/common
så passerer vi en tom gjenstand.
import { Module } from '@nestjs/common';
@Module({})
export class AppModule {}
Nå har vi en Nest.js-modul!
OBS: En modul er en klasse som er merket med en @Module()
dekoratør.
Hver Nest-applikasjon har en rotmodul, som fungerer som et inngangspunkt for å løse en Nest-applikasjons struktur og relasjoner.
Det anbefales sterkt å bruke flere moduler for å organisere applikasjonens komponenter.
De @Module()
decorator gjør det mulig å la utviklere definere metadata om en klasse i Nest-appen.
I tilfellet der det er flere moduler, for eksempel en brukermodul, ordremodul, chattemodul osv app.module.ts
skal brukes til å registrere alle andre moduler i Nest-appen.
Opprette ruter; Kontrollere
Kontrollere er nødvendig for å lage ruter i Nest-applikasjoner. En kontrollers formål er å motta spesifikke forespørsler om en Nest-applikasjon; kontrollere forespørsels- og svarsyklusen for ulike ruter i applikasjonen.
Når en HTTP-forespørsel sendes fra klienten til Nest-applikasjonen, håndterer ruten som samsvarer med ruten der forespørselen blir gjort forespørselen og returnerer det riktige svaret.
For å lage en kontroller i en Nest-app, må vi benytte oss av @Controller()
dekoratør.
på src
katalog, opprett en ny fil app.contoller.ts
, og der kan vi definere en Nest-kontroller:
import { Controller } from '@nestjs/common';
@Controller({})
export class AppController {}
Det er det! Vi har en veldig fin kontroller, men for å lage en ny rute, må vi først gi Nest-appen vår beskjed om den opprettede kontrolleren.
For å oppnå dette sørger vi for å importere AppController
i app.module.ts, og definere informasjon om kontrollerene i @Module()
decorator – som en rekke kontrollere:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
@Module({
controllers: [AppController],
})
export class AppModule {}
Håndtering av GET-forespørsler
Så definerer vi en enkel getUser()
rute (med @Get()
dekorator som brukes til å håndtere HTTP GET-forespørsler til en spesifisert sti) for å tjene som basisruten, kan vi få tilgang til den samme i nettleseren på https://localhost:3000
:
import { Controller, Get } from '@nestjs/common';
@Controller({})
export class AppController {
@Get()
getUser() {
return 'I am a great person';
}
}
Dette resulterer i:
Hmm, her returnerer vi bare en streng, men hva om vi ønsket å returnere et objekt? I stedet for en streng kan vi definere et objekt:
import { Controller, Get } from '@nestjs/common';
@Controller({})
export class AppController {
@Get()
getUser() {
return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
}
}
naviger til http://localhost:3000
i nettleseren din og du vil se objektet:
Bort fra grunnruten, hva med å lage en rute som ligner på http://localhost:3000/user
for å hente alle brukere?
Vi kan lage en kontroller for å håndtere en slik rute på et par måter.
En måte ville være å definere en ny metode ved å bruke @Get()
dekoratør/behandler.
import { Controller, Get } from '@nestjs/common';
@Controller({})
export class AppController {
@Get()
getUser() {
return { name: 'Uchechukwu Azubuko', country: 'Nigeria' };
}
}
Nest.js tilbyr dekoratører eller behandlere for alle de forskjellige HTTP-metodene, inkludert @Get()
, @Post()
, @Put()
, @Delete()
, @Patch()
, @Options()
og @Head()
.
De @All()
decorator definerer et endepunkt som håndterer alle de ulike metodene.
Håndtering av POST-forespørsler
Vi kan også definere POST-forespørsler for lagring av data i databasen ved å bruke @Post()
dekoratør:
import { Controller, Post } from '@nestjs/common';
@Controller({})
export class AppController {
@Post()
store() {
return 'Post request successful';
}
}
Deretter tester vi POST-forespørselen med Postman og legger merke til at strengen returneres som definert.
Du kan spørre, hva om jeg også ønsker å gjøre mer enn å returnere data? Kanskje for å sende data.
For det må du injisere dataene i rutemetoden, som vist:
import { Controller, Post, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller({})
export class AppController {
@Post()
store(@Req() req: Request) {
return req.body;
}
}
Nå, når vi tester POST-forespørselen med Postman, kan vi se dataene som sendes. I dette tilfellet er det bare et tomt objekt:
Sjekk ut vår praktiske, praktiske guide for å lære Git, med beste praksis, bransjeaksepterte standarder og inkludert jukseark. Slutt å google Git-kommandoer og faktisk lære den!
Dynamisk ruting med ruteparametre
Anta at du vil godta dynamiske data som en del av en forespørsel. Først må vi definere tokenet i banen til ruten, for å merke den dynamiske posisjonen på ruten/URL, og deretter bruke @Param()
dekorator, kan ruteparameteren nås slik:
import { Controller, Get, Param } from '@nestjs/common';
@Controller({})
export class AppController {
@Get('/:userId')
getUser(@Param() userId: number) {
return userId;
}
}
De userId
returneres vellykket:
Håndtering av asynkrone forespørsler
Nest.js er i stand til å håndtere asynkrone forespørsler som returnerer et løfte ved å bruke ulike tilnærminger:
import { Controller, Get} from '@nestjs/common';
@Controller({})
export class AppController {
@Get()
async findAll(): Promise {
return [];
}
}
I tilnærmingen ovenfor håndteres asynkronisitet ved å bruke async
nøkkelord. En annen tilnærming er å returnere RxJS observerbare strømmer:
import { Controller, Get} from '@nestjs/common';
@Controller({})
export class AppController {
@Get()
findAll(): Observable {
return of([]);
}
}
Her vil Nest.js abonnere på kilden under panseret, og når strømmen er fullført, tar den den siste utsendte verdien automatisk.
Håndtere omdirigeringer i Nest
De @Redirect()
decorator brukes til å omdirigere et svar til en annen URL. De @Redirect()
decorator godtar to argumenter – URL-en som skal omdirigeres til og statuskoden ved omdirigering, som begge er valgfrie:
import { Controller, Get} from '@nestjs/common';
@Controller({})
export class AppController {
@Get()
@Redirect('https://www.ucheazubuko.com', 302)
getSite() {
return { url: 'https://stackabuse.com' };
}
}
Returnerer statuskode
For å returnere statuskoden for enhver forespørsel som håndteres på Nest.js-serveren, @HttpCode(…)
kommer lett gjennom.
I Nest er standardstatuskoden for GET-forespørsler 200, en POST-forespørsel er 201, en feilforespørsel er 304
Statuskoden for en serverforespørsel kan defineres som vist nedenfor:
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.';
}
}
Håndtering av SLETTE-forespørsler
I likhet med å lage en POST-forespørsel, kan en sletteforespørsel håndteres slik:
import { Controller, Delete, Param } from '@nestjs/common';
@Controller({})
export class AppController {
@Delete('/:userId')
delete(@Param() params: { userId: number }) {
return params;
}
}
Håndtering av oppdateringsforespørsler
En forespørsel om å oppdatere spesifikke data på serveren kan håndteres ved å bruke @Patch()
dekoratør:
import { Controller, Patch, Req} from '@nestjs/common';
import { Request } from 'express';
@Controller({})
export class AppController {
@Patch('/:userId')
update(@Req() req: Request) {
return req.body;
}
}
Nå som vi har sett forskjellige måter å definere typiske kontrollere som vi ofte vil ha på en robust server, er det viktig å merke seg at kontrolleren bør være mager, ren og definert per brukstilfelle, slik at hvis det er en annen kontroller for å definere user
ruter, bør det opprettes en egen katalog og dedikeres for å håndtere det samme - borte fra AppController
.
Så inn user.controller.ts
, kan vi konfigurere alle rutebehandlere til å bli prefiks med /user/
ved å skrive kode som vist nedenfor:
import { Controller, Get } from '@nestjs/common';
@Controller('/user')
export class UserController {
@Get()
getUser() {
return 'I am from the user controller';
}
}
Registrer deg deretter UserController
i kontrollerenes arrays i 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 {}
Når vi navigerer til https:localhost:3000/user
, returnerer den vellykket:
For å holde prosjektmappen enda penere enn den er akkurat nå, kan vi definere en user.module.ts
fil der vi skal definere UserController
:
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
})
export class UserModule {}
Deretter importerer du UserModule
inn 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 {}
Med dette vil vi kunne ha samme effekt som tidligere.
OBS: Nest gjør det enkelt å (g)generere (mo)duler og (co)ntrollere ved hjelp av nest g mo
og nest g co
kommandoer. Spesifikke moduler, for eksempel user
modul og kontrollere kan også opprettes raskt ved hjelp av Nest CLI, ved å kjøre kommandoene: nest g mo user
– for å lage en brukermodul, og nest g co user
– for å opprette en brukerkontroller.
tilbydere
All henting av data fra en database bør håndteres av tilbydere i stedet for kontroller, for å skape et lag med abstraksjon mellom den brukervendte koden og koden som samhandler med potensielt sensitive data. Mellom disse lagene – validering kan settes opp for å sikre riktig databasehåndtering. Med Nest CLI kan vi opprette leverandører ved å generere tjenester:
$ nest g s user
Dette skaper en UserService
der vi ville definere all forretningslogikk for UserController
, Slik at UserController
håndterer kun forespørsler og svar. I user.service.ts
, ser vi at @Injectable()
dekorator brukes til å definere klassen. I Nest er bruken av @Injectable()
decorator er å forvandle tjenester, depoter eller hjelpeklasse til en leverandør.
Leverandører blir injisert i en klasse gjennom konstruktøren. La oss se nærmere på et eksempel.
Tidligere, i user.controller.ts
, vi hadde definert forretningslogikken for å hente brukerobjektet, men nå bør vi definere det samme i UserService
:
import { Controller, Injectable } from '@nestjs/common';
@Controller({})
export class AppController {
@Injectable()
get() {
return { name: 'Uchechukwu Azubuko', country: 'Nigeria'; };
}
}
Neste, i user.controller.ts
fil, la oss definere en konstruktør i UserController
klasse. I denne konstruktøren gir vi en privat userService
, som er en type av UserService
klasse. Det er med denne private vi kan benytte oss av forretningslogikken vi hadde definert tidligere for å hente brukerne:
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();
}
}
Således, jo UserController
klasse, avhenger nå av UserService
klasse i et konsept kjent som
avhengighetsinjeksjon.
På samme måte, logikken i begge user.controller.ts
og user.service.ts
filene oppdateres tilsvarende:
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;
}
}
La oss nå verifisere at endepunktene fungerer som de burde, ved å bruke Postman.
Demystifying Dependency Injection i Nest.js
Når du bygger mindre komponenter i en applikasjon, for eksempel en klasse eller modul, kan klassen din avhenge av funksjonaliteten til en annen klasse eller modul, for eksempel behovet for å benytte seg av en HTTP-tjeneste levert av en annen klasse for å foreta API-anrop, eller tjenestelag som samhandler med utholdenhetslaget.
Avhengigheter kan gis innen kontroller gjennom avhengighetsinjeksjon.
Dependency injection er et programmeringskonsept og -mønster som uttrykker hvordan deler av en applikasjon leveres til andre deler av applikasjonen som krever det, på en slik måte at det gir høy kohesjon, men løs kobling.
Nest støtter avhengighetsinjeksjon, og du kan bruke den i Nest-applikasjonene dine for å forbedre modulariteten til prosjektet ditt.
En praktisk illustrasjon er avbildet slik:
Anta at klasse A bruker en eller annen funksjonalitet av klasse B. Da sies det at klasse A er avhengig av klasse B. For å bruke klasse B i klasse A må vi derfor lage en forekomst av klasse B først (det vil si å lage en Klasse B objekt):
const b = new B ()
.
Å overføre oppgaven med å lage en forekomst av en klasse til en annen klasse og direkte bruke avhengigheten i klassen som er gitt (injektorkomponenten) er kjent som avhengighetsinjeksjon.
Råd: Dependency injection, eller DI, er et av de grunnleggende konseptene i rammeverk som Spring Boot, Nest.js og Angular.js, hvis du vil lese mer om det, kan du sjekke offisiell Angular-dokumentasjon.
Vanligvis bør en klasse utelukkende konsentrere seg om å oppfylle sine funksjoner i stedet for å bli brukt til å lage forskjellige objekter som den kan kreve eller ikke.
Fordeler med avhengighetsinjeksjon.
- Det hjelper med enhetstesting.
- Med avhengighetsinjeksjon reduseres kjelekoden, siden initialiseringen av avhengigheter gjøres av injektorkomponenten.
- Prosessen med å utvide en søknad blir enklere.
- Avhengighetsinjeksjon bidrar til å muliggjøre løs kobling.
Utforsker forespørselsnyttelast
Husk at på forskjellige forespørselsbehandlere som POST og PATCH, kunne vi benytte oss av forespørselen som sendes av serveren ved å bruke @Req()
dekoratør. Det er imidlertid mer til det.
I stedet for å hente hele forespørselsobjektet, kan vi bare trykke på spesifikke deler av forespørselsobjektet som vi trenger.
Dermed tilbyr Nest ulike dekoratører som kan brukes med HTTP-rutebehandlere for å få tilgang til Express of Fastify-objekter:
Reddekoratører | Fastify eller Express-objekt som er tilgjengelig |
`@Request(), @Req()` | `req` |
`@Response(), @Res()` | `re`s` |
`@Next()` | `neste` |
`@Session()` | `req.session` |
`@Param(param?: streng)` | `req.params` / `req.params[param]` |
`@Body(param?: streng)` | `req.body` / `req.body[param]` |
`@Query(param?: streng)` | `req.query` / `req.query[param]` |
`@Headers(param?: streng)` | `req.headers` / `req.headers[param]` |
`@Ip()` | `req.ip` |
`@HostParam()` | `req.hosts` |
Et typisk eksempel vil være å erstatte @Req()
dekoratør som vi brukte tidligere for å få tilgang til hoveddelen av resultatet, med @Body()
som allerede kan gi oss direkte tilgang til hoveddelen av en forespørsel uten å bore:
@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 };
}
I noen tilfeller vil du kanskje bare hente spesifikke egenskaper for en forespørselsnyttelast. I så fall må du definere et DTO-skjema (Data Transfer Object). Dataoverføringsskjemaet er et objekt som definerer en kopi av objektet som hentes, men brukes primært til å overføre dataene mellom objektet som skal lagres eller hentes, og utholdenhetslaget. Vanligvis, siden denne prosessen er mer sårbar for angrep – inneholder ikke DTO så mange sensitive datapunkter. Denne egenskapen lar deg også bare hente visse felt av et objekt.
I Nest anbefales det å bruke klasser for å definere et dataoverføringsobjekt, siden verdien til klassene blir bevart under kompilering.
Anta at hoveddelen av forespørselen hadde et token, og du ikke ønsker å hente eller oppdatere slike data, kan en DTO defineres som vist nedenfor:
@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 };
}
Du vil imidlertid legge merke til at vi har definert typen for updateUserDto
to ganger; i user.service.ts
og i user.controller.ts
, men vi må holde kodene våre DRY (Don't Repeat Yourself) slik at vi ikke gjentar oss selv rundt kodebasen.
For dette, i en ny mappe /user/dto
i /user
katalogen, må vi lage en fil /update-user.dto.ts
med .dto.ts
utvidelse hvor vi definerer og eksporterer UpdateUserDto
klasse for bruk i user.service.ts
og user.controller.ts
filer:
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 };
}
Pipe og validering
Anta at det er behov for å validere dataene som mottas når en forespørsel har blitt sendt over serveren.
I Nest kan vi teste riktigheten av alle data som kommer inn eller ut av applikasjonen ved å bruke pipes som installerer to avhengigheter – class-validator
og class-transformer
.
Et rør er en klasse som er definert med
@Injectable()
dekoratør (dermed er rør leverandører), som implementererPipeTransform
grensesnitt. De transformerer data til ønsket format og evaluerer data slik at hvis dataene blir funnet gyldige, passerer de uendret, ellers blir det kastet et unntak. For å bruke en pipe, må du binde en forekomst av den bestemte pipe-klassen til den aktuelle konteksten.
De class-validator
pakken gjør det mulig å validere dekoratører og ikke-dekoratører, ved hjelp av validator.js internt. Mens class-transformer
pakken gjør det mulig å transformere objekter til forekomster av en klasse, transformere klasse til objekt, og serialisere eller deserialisere objekter basert på visse kriterier.
De åtte rørene levert av Nest er:
ValidationPipe
ParseArrayPipe
ParseIntPipe
ParseUUIDPipe
ParseBoolPipe
DefaultValuePipe
ParseEnumPipe
ParseFloatPipe
For å demonstrere validering i Nest i denne veiledningen, bruker vi den innebygde ValidationPipe
som gjør det mulig å håndheve validering på forespørselsnyttelast og kombineres godt med class-validator
pakke; spesifikke regler er deklarert med enkle merknader i Data Transfer Object/lokal klassedeklarasjoner i hver modul.
For å begynne å bruke den innebygde ValidationPipe
som eksporteres fra @nestjs/common
, la oss installere class-validator
og class-transformer
pakker:
$ npm i --save class-validator class-transformer
# Or
$ yarn add class-validator class-transformer
# Or
$ pnpm install class-validator class-transformer
Deretter navigerer du til main.ts
hvor vi skal binde ValidationPipe
på rotnivået til applikasjonen for å sikre at alle endepunkter i appen vår er beskyttet mot å hente ugyldige data:
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();
Deretter legger vi til noen få valideringsregler i dataoverføringsobjektdeklarasjonene for hver modul ved å deklarere de riktige datasjekkene for hver enkelt data. I vårt tilfelle vil vi erklære passende valideringsregler for name
og email
in UpdateUserDto
:
import { IsEmail, IsString } from 'class-validator';
export class UpdateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
}
De @IsString()
dekorator sjekker om en gitt data er en ekte streng, og @IsEmail()
validator sjekker om en gitt data er en e-post, ellers returnerer den falsk og kaster et unntak.
Nå, hvis vi prøver å lage en PATCH
forespørsel til en brukerprofil, og skriv inn et nummer i stedet for en gyldig e-post, for eksempel vil et unntak bli kastet:
Med disse har vi en veldig fin validering i Nest-appen vår.
Mens du validerer med ValidationPipe
, er det også mulig å filtrere egenskapene våre som vi ikke vil at metodebehandleren vår skal motta. For eksempel hvis vår behandler bare forventer name
og email
eiendommer, men en forespørsel omfatter også en country
eiendom, kan vi fjerne country
egenskap fra det resulterende objektet ved innstilling whitelist
til true
når vi instansierer 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();
Binding av rør på metodeparameternivå
Rør kan også defineres på params
, også. For dette vil vi binde røret på metodens param-nivå.
Før nå, selv om vi definerte userId
for å være et tall, vil du legge merke til at hvis vi gjør en forespørsel med userId
som en streng, viser det seg vellykket uansett:
For å sikre at verdien av userId
må alltid være et tall, vil vi erklære det bindende getUser()
metodebehandler med en valideringssjekk som sikrer det samme:
...
import { ParseIntPipe } from '@nestjs/common';
@Get('/:userId')
getUser(@Param('userId', ParseIntPipe) userId: number) {
return this.userService.getUser(userId);
}
getUser(userId: number) {
return { userId };
}
De ParseIntPipe
definerer den innebygde ParseInt Pipe og sikrer at dataene den kjøres mot må være et heltall.
Nå, når vi lager en GET
forespørsel til en ugyldig userId
av streng "ab", mislykkes valideringen og et unntak blir kastet med en 400
statuskode:
Men med en numerisk verdi passerer valideringen vellykket:
Vi kan også oppdatere andre metodebehandlere tilsvarende for å sikre riktig validering:
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 };
}
}
Nå har vi sikret den beste praksis-teknikken for å validere data som kommer inn i applikasjonen vår, kanskje fra en ekstern kilde, når som helst.
konklusjonen
I denne veiledningen har du vært i stand til å lære om det siste barnet på Node.js-blokken; Nest.js, og alt som kreves for å hjelpe deg i gang hvis du ønsker å bygge en applikasjon med den. Du har lært hva Nest er, dets funksjoner, hvordan du oppretter et Nest-prosjekt, hvordan du håndterer innkommende data i en Nest-app og hvordan du validerer innkommende data. Alt i alt har du lært om byggeklossene til alle Nest-applikasjoner, og verdien som hver komponent tilfører en Nest.js-applikasjon.
Fra dette tidspunktet er det fortsatt mye å lære når det gjelder å bygge en bedriftsapplikasjon med Nest, men du har klart å dekke grunnleggende konsepter som kan få deg i gang med alt som ligger foran deg.
Se opp for en ny guide i fremtiden, der vi lærer hvordan du bygger et avslappende API med Nest og MySQL.
Takk for lesing!