Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Guida a Nest.js: creazione di un'API REST con Nest e Node

Come sviluppatore backend di Node.js, sarai d'accordo sul fatto che, per impostazione predefinita, Node.js è molto semplice e non fa ipotesi su ciò di cui hai bisogno durante la creazione di un'app. Di conseguenza, sei responsabile della configurazione di tutto ciò che desideri utilizzare in un'app, inclusa la gestione del routing, l'esecuzione di chiamate API, la configurazione di TypeScript o Web Socket o anche cose fondamentali come l'organizzazione del codice, la struttura dei file e le convenzioni di denominazione. .

Gestire un'applicazione su larga scala può essere un compito difficile, soprattutto se non è stata progettata con una struttura chiara e linee guida rigorose per l'organizzazione del codice.

Nest.js cerca di affrontare alcuni di questi problemi creando un'astrazione attorno a Node.js in modo che tu come sviluppatore possa concentrarti sul problema dell'applicazione piuttosto che su altri piccoli dettagli di implementazione.

In questa guida imparerai i fondamenti fondamentali di Nest.js da cima a fondo, con l'obiettivo di aggiornarti in modo da poter creare applicazioni Node.js di livello aziendale con l'aiuto di Nest.js in pochissimo tempo.

Tutto quello che impareremo attraverso questa guida sarà incrementale; coprendo molto terreno sui concetti introduttivi. Per ottenere il massimo da questa guida, è utile programmare insieme.

Immergiamoci subito, gente!

Codice sorgente: Come al solito, puoi biforcare e armeggiare con il codice sorgente ospitato su GitHub.

Nota: Utilizzeremo Postman per testare l'API nella nostra demo. Puoi scaricarlo sul Pagina di download del postino. In alternativa, puoi semplicemente utilizzare il browser, la riga di comando curl strumento o qualsiasi altro strumento con cui potresti avere familiarità.

Cos'è Nest.js

Pensa a Nest.js come a un superset di Node.js che elimina attività difficili, strumenti e codice boilerplate, aggiungendo allo stesso tempo un toolkit completo per lo sviluppo di applicazioni utilizzando JavaScript e TypeScript moderni.

Nest.js fornisce un'architettura applicativa pronta all'uso che consente agli sviluppatori e ai team di creare soluzioni altamente scalabili, testabili, liberamente accoppiate e di facile manutenzione, sfruttando opzioni e moduli facilmente disponibili e importanti nella community, come quelli disponibili in Applicazioni Express.js. Potresti anche scambiare Express (che utilizza nascosto per impostazione predefinita) con Fastify, ma farlo significherebbe che potresti dover utilizzare diverse librerie compatibili con Fastify nella tua applicazione.

Combina le caratteristiche della programmazione funzionale, della programmazione orientata agli oggetti e della programmazione reattiva funzionale e con più di 52.4k stelle e 6.2k fork su GitHub e un numero di download settimanali fino a 1,784,004, il framework progressivo Node.js è un punto di riferimento popolare per creare applicazioni lato server efficienti, scalabili e di livello aziendale.

Caratteristiche di Nest.js

Di seguito sono riportati i motivi per cui Nest.js è cresciuto fino a diventare un framework Node.js così popolare:

  1. Nest.js è stato creato per aiutare gli sviluppatori a creare sia applicazioni monolitiche che microservizi.
  2. Sebbene sia potente, è anche facile da usare per gli sviluppatori; facile da usare, veloce da apprendere e facile da applicare.
  3. Sfrutta TypeScript (un superset di JavaScript) pronto all'uso e lascia spazio agli sviluppatori per scrivere codice manutenibile esente da errori di runtime.
  4. Possiede un'interfaccia a riga di comando che aiuta ad aumentare la produttività degli sviluppatori e la facilità di sviluppo.
  5. Quando si crea con Nest.js, i processi di sviluppo vengono migliorati e si risparmia tempo sia che si stia avviando un prodotto minimo vitale o che si stia lavorando su un'applicazione perché Nest è dotato di una straordinaria struttura di cartelle di progetto per impostazione predefinita.
  6. Supporta una varietà di moduli specifici di Nest che aiutano nell'integrazione di concetti e tecnologie comuni tra cui TypeORM, GraphQL, registrazione, convalida, Mongoose, WebSocket, memorizzazione nella cache, ecc.
  7. Nest.js può vantarsi di contenere la migliore documentazione per qualsiasi framework disponibile. La sua documentazione è approfondita, facile da comprendere e utile per risparmiare tempo di debug, poiché arriva senza sforzo quando è necessaria una soluzione a un problema.
  8. Nest.js si integra con Jest, il che semplifica la scrittura di unit test sulle tue applicazioni.
  9. È progettato per applicazioni aziendali sia su piccola che su larga scala.

Creazione di un progetto Nest.js

Per iniziare con Nest.js sul tuo computer locale, devi prima installare Nest Command Line Interface (CLI), che aiuterebbe a strutturare una nuova cartella di progetto Nest.js e popolare la cartella con i file principali e i moduli necessari per un Applicazione Nest.js.

Esegui il comando seguente per installare l'interfaccia della riga di comando Nest.js:

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

Dopo aver installato correttamente la CLI Nest.js a livello globale sul tuo computer locale, puoi eseguirla nest sulla riga di comando per vedere i vari comandi a cui possiamo attingere:

$ nest

Risultati in:

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                 │
      └───────────────┴─────────────┴──────────────────────────────────────────────┘

Qui ti viene mostrato come utilizzare i comandi e ora puoi accedere al file new|n [options] [name] comando per creare il tuo primo progetto Nest.js:

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

Successivamente, ti verrà chiesto quale gestore di pacchetti desideri utilizzare:

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

Sentiti libero di scegliere il gestore pacchetti che preferisci, io andrò con pnpm. Questo perché è circa tre volte più efficiente e veloce di NPM e, con un sistema di cache veloce, PNPM è anche più veloce di Yarn.

Dopo aver scelto un gestore pacchetti, il processo di installazione continua, quindi verrà creata l'app Nest.js.

Ora, è possibile cd nel progetto appena creato e aprilo con un editor a tua scelta:

$ cd getting-started-with-nestjs

Una volta creato il progetto, possiamo eseguirlo con uno dei seguenti comandi:

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

Se dai un'occhiata al file package.json file, noterai nel segmento dello script, il valore di pnpm run start is nest start:


    
"start": "nest start",

Ciò significa che puoi anche eseguire l'app Nest.js eseguendo:

$ nest start

Uno sguardo alla struttura del progetto Nest.js

Diamo uno sguardo più da vicino a come è strutturata un'app Nest:

/package.json

Il package.json è il cuore del progetto Node.js e, per estensione, del progetto Nest.js. Contiene tutti i metadati del progetto e definisce varie proprietà funzionali del progetto necessarie per installare le dipendenze dell'applicazione o eseguire script di progetto.

Abbiamo già visto la capacità del start script.

Il start:dev profile rende possibile controllare le modifiche nell'applicazione e ricaricarla automaticamente, senza la necessità di arrestarla e riavviarla, ed è pensato per lo sviluppo. IL start:prod Lo script è utile quando vuoi verificare se la tua applicazione è pronta per la produzione e quando la distribuisci in produzione, insieme ad altri script per testare l'app Nest.js.

@nestjs/platform-express definisce express come server HTTP predefinito in un'applicazione Nest.

/tsconfig.json

Il tsconfig.json file è un file scritto in JSON (JavaScript Object Notation) che definisce le opzioni relative a TypeScript richieste per compilare l'app Nest.

/nest-cli.json

Contiene i metadati necessari per creare, organizzare o distribuire le applicazioni Nest.

/test

Questa directory contiene tutti i file necessari per eseguire i test Nest. Nest utilizza il framework Jest per eseguire test con la configurazione Jest nel file jest-e2e.json file.

/src

Il src directory è la cartella principale del nucleo del progetto Nest. Tiene il main.ts file che è il file in cui viene avviata l'app Nest. Il lavoro del main.ts il file deve essere caricato AppModule da cui viene importato /src/app.module.ts.

Più avanti in questa guida impareremo a conoscere i moduli; uno dei componenti principali di un'applicazione Nest.js.

Il AppModule è una classe creata come modulo, utilizzando il file @Module decoratore. Nel app.module.ts file, AppService da ./app.service ed AppController da ./app.controller vengono anche importati.

Il AppController è anche una classe creata utilizzando il file @Controller decoratore, mentre il AppService è una classe creata utilizzando il file @Injectable annotazione.

La cosa bella di Nest è che ha pochissimi decoratori al suo interno che aggiungono metadati a qualsiasi classe e che i metadati definiscono lo scopo di quella classe, in modo tale che:

  • @Controller()trasforma una classe in un controller.
  • @Module() trasforma una classe in un modulo.
  • @Injectable() trasforma una classe in un provider.

Anche nel src la directory è la app.controller.spec.ts file, che è un file di test per i controller.

Possiamo eseguire l'app utilizzando nest start.

L'applicazione inizia alle http://localhost:3000 sul tuo browser:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Possiamo modificare il contenuto visualizzato su http://localhost:3000, andando al app.service.ts file, dove è stato definito il provider per la route dell'indice.

Gli elementi costitutivi di un'app Nest.js

Esistono tre componenti principali di un'applicazione Nest.js:

  1. moduli
  2. Controller
  3. provider

Per conoscere gli elementi costitutivi di un'app Nest, ripuliamo innanzitutto il progetto Nest eliminando il file app.controller.spec.ts, ./app.service, app.module.tse ./app.controller File; lasciando solo main.ts, per emulare un ciclo di vita di sviluppo da zero.

A questo punto, quando rimuoviamo il file importato AppModule file da main.ts, ci viene chiesto di non fornire un argomento per "modulo".

Per dimostrare gli elementi costitutivi di un'app Nest, daremo un'occhiata a una semplice implementazione del profilo utente, creando un'API REST per gestire le operazioni CRUD su un oggetto.

moduli

Nel src cartella creane una nuova app.module.ts file, quindi creare un file AppModule classe, che esportiamo.

Successivamente, importa il file AppModule classe in main.ts, e corri nest start.

Spostarsi http://localhost:3000 nel tuo browser e riceverai un errore 404:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Questo perché non abbiamo ancora definito un percorso per l'URL di base dell'app Nest.

Già nel app.module.ts, abbiamo il AppModule che abbiamo non è ancora un modulo Nest. Per renderlo un modulo Nest, aggiungiamo il @Module() decoratore da cui viene importato @nestjs/commonquindi passiamo un oggetto vuoto.



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

export class AppModule {}

Ora abbiamo un modulo Nest.js!

Nota: Un modulo è una classe annotata con a @Module() decoratore.

Ogni applicazione Nest dispone di un modulo root, che funge da punto di ingresso per risolvere la struttura e le relazioni di un'applicazione Nest.

Si consiglia vivamente di utilizzare più moduli per organizzare i componenti dell'applicazione.

Il @Module() decorator consente agli sviluppatori di definire metadati su una classe nell'app Nest.

Nel caso in cui siano presenti più moduli, come modulo utenti, modulo ordini, modulo chat, ecc., il file app.module.ts deve essere utilizzato per registrare tutti gli altri moduli dell'app Nest.

Creazione di percorsi; Controllori

I controller sono necessari per creare percorsi nelle applicazioni Nest. Lo scopo di un controller è ricevere richieste specifiche per un'applicazione Nest; controllare il ciclo di richiesta e risposta per vari percorsi all'interno dell'applicazione.

Quando viene effettuata una richiesta HTTP dal client all'applicazione Nest, il percorso che corrisponde al percorso in cui viene effettuata la richiesta gestisce la richiesta e restituisce la risposta appropriata.

Per creare un controller in un'app Nest, dobbiamo utilizzare il file @Controller() decoratore.

Nel src directory, creare un nuovo file app.contoller.ts, e qui possiamo definire un controller Nest:

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

@Controller({})

export class AppController {}

Questo è tutto! Abbiamo un controller molto carino, ma per creare un nuovo percorso dobbiamo prima comunicare alla nostra app Nest il controller creato.

Per raggiungere questo obiettivo, ci assicuriamo di importare AppController in app.module.ts e definire le informazioni sui controller in @Module() decoratore – come un array di controller:



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

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

export class AppModule {}

Gestione delle richieste GET

Quindi definiamo un semplice getUser() percorso (con il @Get() decoratore utilizzato per gestire le richieste HTTP GET a un percorso specificato) per fungere da percorso di base, possiamo accedere allo stesso sul browser all'indirizzo https://localhost:3000:



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

@Controller({})

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

Questo risulta in:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Hmm, qui stiamo restituendo solo una stringa, ma cosa succederebbe se volessimo restituire un oggetto? Invece di una stringa, possiamo definire un oggetto:



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

@Controller({})

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

Spostarsi http://localhost:3000 nel tuo browser e vedrai l'oggetto:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Lontano dal percorso di base, che ne dici di creare un percorso simile a http://localhost:3000/user per recuperare tutti gli utenti?

Possiamo creare un controller per gestire tale percorso in un paio di modi.

Un modo potrebbe essere quello di definire un nuovo metodo, utilizzando il file @Get() decoratore/gestore.

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

@Controller({})

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

Nest.js fornisce decoratori o gestori per tutti i vari metodi HTTP inclusi @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options()e @Head().

Il @All() decorator definisce un endpoint che gestisce tutti i vari metodi.

Gestione delle richieste POST

Possiamo anche definire richieste POST per la memorizzazione dei dati nel database, utilizzando il file @Post() decoratore:

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

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

Quindi, testiamo la richiesta POST utilizzando Postman e notiamo che la stringa viene restituita correttamente come definito.

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Potresti chiederti: cosa succede se volessi fare di più oltre alla semplice restituzione dei dati? Forse, per inviare dati.

Per questo, è necessario inserire i dati all'interno del metodo route, come mostrato:

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

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

Ora, quando testiamo la richiesta POST con Postman, siamo in grado di visualizzare i dati che vengono inviati. In questo caso, è solo un oggetto vuoto:

Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Routing dinamico con parametri di percorso

Supponiamo che tu voglia accettare dati dinamici come parte di una richiesta. Per prima cosa dobbiamo definire il token nel percorso della rotta, in modo da notare la posizione dinamica sulla rotta/URL, quindi utilizzando il @Param() decoratore, è possibile accedere al parametro del percorso in questo modo:

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

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

Il userId viene restituito con successo:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Gestione delle richieste asincrone

Nest.js è in grado di gestire richieste asincrone che restituiscono una promessa utilizzando vari approcci:

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

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

Nell'approccio precedente, l'asincronicità viene gestita utilizzando il metodo async parola chiave. Un altro approccio consiste nel restituire flussi osservabili RxJS:

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

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

Qui, Nest.js si iscriverà alla sorgente dietro le quinte e, una volta completato lo streaming, prenderà automaticamente l'ultimo valore emesso.

Gestione dei reindirizzamenti in Nest

Il @Redirect() decorator viene utilizzato per reindirizzare una risposta a un URL diverso. IL @Redirect() Il decoratore accetta due argomenti: l'URL a cui reindirizzare e il codice di stato al momento del reindirizzamento, entrambi facoltativi:

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

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

Codice di stato restituito

Per restituire il codice di stato per qualsiasi richiesta gestita sul server Nest.js, il file @HttpCode(…) passa facilmente.

In Nest, il codice di stato predefinito per le richieste GET è 200, una richiesta POST è 201, una richiesta di errore è 304

Il codice di stato per una richiesta del server può essere definito come mostrato di seguito:

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.';
  }
}

Gestione delle richieste DELETE

Similmente a una richiesta POST, una richiesta di eliminazione può essere gestita in questo modo:

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

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

Gestione delle richieste di aggiornamento

Una richiesta di aggiornamento di dati specifici sul server può essere gestita utilizzando il file @Patch() decoratore:

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

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

Ora che abbiamo visto vari modi per definire i controller tipici che avremmo spesso su un server robusto, è importante notare che il controller dovrebbe essere snello, pulito e definito per caso d'uso, in modo tale che se esiste un altro controller per definire user percorsi, è necessario creare e dedicare una directory separata per la gestione degli stessi, lontano dal file AppController.

Quindi in user.controller.ts, possiamo configurare tutti i gestori di percorso in essi contenuti con il prefisso /user/ scrivendo il codice come mostrato di seguito:



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

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

Successivamente, registrati UserController negli array dei controller in 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 {}

Quando navighiamo verso https:localhost:3000/user, restituisce con successo:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Per mantenere la cartella del progetto ancora più ordinata di quanto non sia in questo momento, possiamo definire a user.module.ts file in cui definiremo il file UserController:

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

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

export class UserModule {}

Quindi, importa UserModule ai miglioramenti 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 {}

In questo modo potremo ottenere lo stesso effetto di prima.

Nota: Nest semplifica la (g)generazione di (mo)duli e (co)ntroller utilizzando il nest g mo ed nest g co comandi. Moduli specifici, come il user modulo e controller possono anche essere creati rapidamente utilizzando la CLI Nest, eseguendo i comandi: nest g mo user – per creare un modulo utente, e nest g co user – per creare un controller utente.

provider

Tutto il recupero dei dati da un database dovrebbe essere gestito dai fornitori anziché dai controllori, per creare uno strato di astrazione tra il codice rivolto all'utente e il codice che interagisce con dati potenzialmente sensibili. Tra questi livelli è possibile impostare la convalida per garantire la corretta gestione del database. Con la CLI Nest possiamo creare fornitori generando servizi:

$ nest g s user

Questo crea a UserService in cui definiremmo tutta la logica aziendale per il UserController, Cosicché UserController gestisce solo richieste e risposte. In user.service.ts, vediamo che il @Injectable() decoratore viene utilizzato per definire la classe. In Nest, l'uso di @Injectable() decorator è trasformare servizi, repository o classi helper in un provider.

I provider vengono inseriti in una classe tramite il relativo costruttore. Diamo uno sguardo più da vicino a un esempio.

In precedenza, in user.controller.ts, avevamo definito la logica aziendale per ottenere l'oggetto utente, ma ora dovremmo definire la stessa cosa nel file UserService:



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

@Controller({})

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

Successivamente, nel user.controller.ts file, definiamo un costruttore nel file UserController classe. In questo costruttore forniamo un private userService, che è un tipo di UserService classe. È con questo privato che possiamo attingere alla logica aziendale che avevamo definito in precedenza per recuperare gli utenti:



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();
  }
}

Così, la UserController classe, ora dipende da UserService classe in un concetto noto come
iniezione di dipendenza.

Allo stesso modo, la logica in entrambi user.controller.ts ed user.service.ts i file vengono aggiornati di conseguenza:



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;
  }
}

Ora verifichiamo che gli endpoint funzionino come dovrebbero, utilizzando Postman.

Demistificazione dell'iniezione di dipendenza in Nest.js

Quando si creano componenti più piccoli di un'applicazione, come una classe o un modulo, la classe potrebbe dipendere dalla funzionalità di un'altra classe o modulo, ad esempio la necessità di attingere a un servizio HTTP fornito da una classe diversa per effettuare chiamate API o livelli di servizio che interagiscono con il livello di persistenza.

Le dipendenze possono essere fornite all'interno dei controller tramite iniezione di dipendenza.

L'inserimento delle dipendenze è un concetto e un modello di programmazione che esprime il modo in cui parti di un'applicazione vengono consegnate ad altre parti dell'applicazione che le richiedono, in modo tale da fornire un'elevata coesione ma un accoppiamento lento.

Nest supporta l'inserimento delle dipendenze e puoi utilizzarlo nelle tue applicazioni Nest per migliorare la modularità del tuo progetto.

Un esempio pratico è rappresentato in questo modo:

Supponiamo che la classe A utilizzi alcune funzionalità della classe B. Allora si dice che la classe A dipende dalla classe B. Pertanto, per poter utilizzare la classe B nella classe A, dobbiamo prima creare un'istanza della classe B (ovvero, creare un Oggetto di classe B): const b = new B ().
Il trasferimento del compito di creare un'istanza di una classe a un'altra classe e di utilizzare direttamente la dipendenza nella classe fornita (il componente iniettore) è noto come iniezione di dipendenza.

Consigli: L'iniezione di dipendenza, o DI, è uno dei concetti fondamentali in framework come Spring Boot, Nest.js e Angular.js, se desideri saperne di più, puoi controllare il documentazione ufficiale di Angular.

In genere, una classe dovrebbe concentrarsi esclusivamente sull'adempimento delle sue funzioni anziché essere utilizzata per creare vari oggetti che potrebbe richiedere o meno.

Vantaggi dell'iniezione delle dipendenze.

  1. Aiuta con i test unitari.
  2. Con l'iniezione delle dipendenze, il codice boilerplate viene ridotto, poiché l'inizializzazione delle dipendenze viene eseguita dal componente iniettore.
  3. Il processo di estensione di un'applicazione diventa più semplice.
  4. L'inserimento delle dipendenze aiuta a consentire un accoppiamento lento.

Esplorazione dei payload delle richieste

Ricorda che su vari gestori di richieste come POST e PATCH, siamo stati in grado di attingere alla richiesta inviata dal server utilizzando il metodo @Req() decoratore. Tuttavia c’è di più.

Invece di recuperare l'intero oggetto della richiesta, possiamo semplicemente attingere alle parti specifiche dell'oggetto della richiesta di cui abbiamo bisogno.
Pertanto, Nest fornisce vari decoratori che possono essere utilizzati con i gestori di route HTTP per accedere agli oggetti Express di Fastify:

Decoratori di nidi Oggetto Fastify o Express a cui si accede
`@Request(), @Req()` "richiesto".
`@Response(), @Res()` `re“s`
`@Avanti()` "prossimo".
"@Sessione()". `req.session`
`@Param(param?: stringa)` `req.params` / `req.params[param]`
`@Body(parametro?: stringa)` `req.body` / `req.body[param]`
`@Query(parametro?: stringa)` `req.query` / `req.query[parametro]`
`@Headers(parametro?: stringa)` `req.headers` / `req.headers[param]`
`@Ip()` `req.ip`
`@HostParam()` "req.hosts".

Un tipico esempio potrebbe essere la sostituzione di @Req() decoratore che abbiamo utilizzato in precedenza per accedere al corpo del risultato, con il file @Body() che può già darci accesso diretto al corpo di una richiesta senza perforare:



@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 };
}

In alcuni casi, potresti voler recuperare solo proprietà specifiche di un payload di richiesta. In tal caso, dovresti definire uno schema DTO (Data Transfer Object). Il Data Transfer Schema è un oggetto che definisce una copia dell'oggetto da recuperare, ma viene utilizzato principalmente per trasferire i dati tra l'oggetto che dovrebbe essere salvato o recuperato e il livello di persistenza. In genere, poiché questo processo è più vulnerabile agli attacchi, il DTO non contiene tanti punti dati sensibili. Questa caratteristica consente inoltre di recuperare solo determinati campi di un oggetto.

In Nest, si consiglia di utilizzare le classi per definire un oggetto di trasferimento dati, poiché il valore delle classi viene preservato durante la compilazione.

Supponendo che il corpo della richiesta contenga un token e non si desideri recuperare o aggiornare tali dati, è possibile definire un DTO come mostrato di seguito:



@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 };
}

Tuttavia, noterai che abbiamo definito il tipo per updateUserDto due volte; In user.service.ts e in user.controller.ts, ma dobbiamo mantenere i nostri codici DRY (Don't Repeat Yourself) in modo da non ripeterci nella codebase.

Per questo, in una nuova cartella /user/dto nel /user directory, dobbiamo creare un file /update-user.dto.ts con la .dto.ts estensione in cui definiamo ed esportiamo il file UpdateUserDto classe da utilizzare in user.service.ts ed user.controller.ts File:



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 };
}

Tubo e convalida

Supponiamo che sia necessario convalidare i dati ottenuti quando è stata effettuata una richiesta sul server.

In Nest, possiamo testare la correttezza di tutti i dati che entrano o escono dall'applicazione utilizzando pipe che installano due dipendenze: class-validator ed class-transformer.

Una pipe è una classe definita con il file @Injectable() decoratore (quindi le pipe sono fornitori), che implementa il PipeTransform interfaccia. Trasformano i dati nel formato desiderato e valutano i dati in modo tale che, se i dati vengono ritenuti validi, passano invariati, altrimenti viene generata un'eccezione. Per utilizzare una pipe, è necessario associare un'istanza della particolare classe di pipe al contesto appropriato.

Il class-validator Il pacchetto rende possibile convalidare decoratori e non decoratori, utilizzando validatore.js internamente. Mentre il class-transformer Il pacchetto rende possibile trasformare oggetti in istanze di una classe, trasformare classi in oggetti e serializzare o deserializzare oggetti in base a determinati criteri.

Gli otto tubi forniti da Nest sono:

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

Per dimostrare la convalida in Nest in questa guida, utilizzeremo il built-in ValidationPipe che rende possibile applicare la convalida sui payload delle richieste e si combina bene con il file class-validator pacchetto; regole specifiche vengono dichiarate con semplici annotazioni nelle dichiarazioni dell'oggetto trasferimento dati/classe locale in ciascun modulo.

Per iniziare a utilizzare il built-in ValidationPipe da cui viene esportato @nestjs/common, installiamo il file class-validator ed class-transformer Pacchetti:

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

Quindi, vai a main.ts dove ci legheremo ValidationPipe a livello root dell'applicazione per garantire che tutti gli endpoint nella nostra app siano protetti dal recupero di dati non validi:



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();

Successivamente, nelle dichiarazioni dell'oggetto Data Transfer di ciascun modulo, aggiungiamo alcune regole di convalida dichiarando i controlli dei dati appropriati per ogni singolo dato. Nel nostro caso, dichiareremmo regole di convalida appropriate per name ed email in UpdateUserDto:



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

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

  @IsEmail()
  email: string;
}

Il @IsString() Il decoratore controlla se un dato dato è una stringa reale e il file @IsEmail() il validatore controlla se un determinato dato è un'e-mail, altrimenti restituisce false e genera un'eccezione.

Ora, se proviamo a creare a PATCH richiesta a un profilo utente e inserisci un numero invece di un'e-mail valida, ad esempio, verrà generata un'eccezione:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Con questi, abbiamo un'ottima convalida nella nostra app Nest.

Durante la convalida con ValidationPipe, è anche possibile filtrare le nostre proprietà che non vogliamo che il nostro gestore del metodo riceva. Ad esempio, se il nostro gestore si aspetta solo name ed email proprietà, ma una richiesta include anche a country proprietà, possiamo rimuovere il file country proprietà dall'oggetto risultante impostando whitelist a true quando istanziamo 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();

Associazione dei tubi a livello di parametro del metodo

È inoltre possibile definire i tubi params, anche. Per questo, collegheremo la pipe a livello di parametro del metodo.

Prima d'ora, anche se abbiamo definito il userId essere un numero, noteresti che se facciamo una richiesta con il userId come stringa, risulta riuscita indipendentemente:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Per garantire che il valore di userId deve essere sempre un numero, lo dichiareremo vincolante getUser() gestore del metodo con un controllo di convalida che garantisce lo stesso:


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

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


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

Il ParseIntPipe definisce la ParseInt Pipe integrata e garantisce che i dati su cui viene eseguito debbano essere un numero intero.

Ora, quando facciamo a GET richiesta ad un invalido userId della stringa “ab”, la validazione fallisce e viene lanciata un'eccezione con a 400 codice di stato:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Ma con un valore numerico, la convalida passa con successo:

Guida a Nest.js - Creazione di un'API REST con Nest e Node PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Possiamo anche aggiornare di conseguenza altri gestori di metodi per garantire una validazione adeguata:



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 };
  }
}

Ora abbiamo garantito la tecnica best practice per la convalida dei dati che entrano nella nostra applicazione, magari da una fonte esterna, in qualsiasi momento.

Conclusione

In questa guida, hai potuto conoscere l'ultimo bambino del blocco Node.js; Nest.js e tutto ciò che è necessario per aiutarti a iniziare se desideri creare un'applicazione con esso. Hai imparato cos'è Nest, le sue funzionalità, come creare un progetto Nest, come gestire i dati in ingresso in un'app Nest e come convalidare i dati in ingresso. Nel complesso, hai imparato a conoscere gli elementi costitutivi di qualsiasi applicazione Nest e il valore che ciascun componente apporta a un'applicazione Nest.js.

Da questo punto, c'è ancora molto da imparare riguardo alla creazione di un'applicazione di livello aziendale con Nest, ma sei stato in grado di coprire con successo concetti fondamentali che possono consentirti di essere operativo verso tutto ciò che ti aspetta.

Fai attenzione a una nuova guida in futuro, in cui impareremo come creare un'API riposante con Nest e MySQL.

Grazie per la lettura!

Risorse addizionali

Nest.js Documenti
Documenti angolari

Timestamp:

Di più da Impilamento