Che cos'è NestJS?
NestJS è un moderno framework NodeJS che utilizza sotto il cofano i più diffusi framework NodeJS come Express e Fastify. NestJS è stato ampiamente ispirato da Angular e, di conseguenza, utilizza un sistema di moduli in stile Angular. NestJS è scritto in TypeScript, sebbene supporti anche JavaScript nativo.
Prerequisiti
Per seguire questo tutorial, devi soddisfare i seguenti requisiti
- Competenza in PostMan o in qualsiasi altro strumento di test API.
- Conoscenza di base delle app NodeJS ed Express.
- Conoscenza di base di TypeScript.
- Competenza in MongoDB(Mangusta).
Quanto segue dovrebbe essere installato sul tuo sistema
- NodeJS v.14 e versioni successive.
- Codice Visual Studio (consigliato) o qualsiasi altro IDE.
- PostMan o qualsiasi altro strumento di test API.
Terminologie comuni utilizzate in NestJS;
Ecco alcuni dei termini utilizzati più regolarmente in NestJS che incontrerai spesso in questo articolo.
Interfacce
Un'interfaccia è una definizione di tipo. Di conseguenza, viene utilizzato come controllo/enforcer del tipo in funzioni, classi, ecc.
interface humanInterface{
name:string;
gender:string;
age:number;
}
const kevin: humanInterface={
name:'Kevin Sunders',
gender:'Male',
age: 25,
}
L'humanInterface sopra esegue un controllo rigoroso del tipo su kevin oggetto. Typescript genererebbe un errore se aggiungessi un altro campo o cambiassi il tipo di una qualsiasi delle proprietà dell'oggetto.
Titolari del trattamento
I responsabili del trattamento sono incaricati di ricevere le richieste in arrivo e di rispondere al cliente. Un titolare del trattamento collabora con il servizio associato.
Servizi
Un servizio è un fornitore che archivia e recupera dati e viene utilizzato con il relativo titolare del trattamento.
Decoratori
Un decoratore è un'espressione che restituisce una funzione che accetta un target , name e property descriptor come argomenti facoltativi. I decoratori sono scritti come @decorator-name . Di solito sono allegati a dichiarazioni di classe, metodi e parametri.
@Get()
getAll(): Model[] {
return this.testService.getAll();
}
Il @Get decorator sopra contrassegna il blocco di codice sotto di esso come GET richiesta. Ne parleremo più avanti.
Modulo
Un modulo è una parte di un programma che gestisce un compito particolare. Un modulo in NestJS è contrassegnato annotando una classe annotata con @Module() decoratore. Nest utilizza i metadati forniti da @Module() decoratore per organizzare la struttura dell'applicazione.
Installazione della CLI
Per iniziare dovrai installare NestJS CLI ****con npm . Puoi saltare questo passaggio se hai già installato l'interfaccia a riga di comando NestJS sul tuo sistema.
npm i -g @nestjs/cli
Questo blocco di codice sopra installerà la CLI nest a livello globale sul tuo sistema.
Creazione di un nuovo progetto
Per generare un nuovo progetto, esegui nest new seguito dal nome del progetto desiderato. Per questo articolo, scriveremo una semplice API per blog con funzionalità CRUD aderendo agli standard RESTful.
nest new Blog-Api
Questo comando ti chiederà di selezionare un gestore di pacchetti, scegli npm .
Questo quindi impalcherà l'intera struttura del progetto con un endpoint API di test la cui porta è impostata su 3000 per impostazione predefinita. Puoi testarlo su https://localhost:3000 dopo aver eseguito npm run start:dev comando che avvierà il server in modalità di controllo simile a quello che fa nodemon nelle app express.
Dopo aver testato l'endpoint, dovrai eliminare alcuni dei file predefiniti perché non ne avrai più bisogno. Per fare questo;
- apri la cartella src e dentro,
- elimina
app.controller.spec.ts, - elimina
app.controller.ts, - elimina
app.service.ts, - Apri
app.module.ts, - Rimuovi il riferimento a
AppControllerneicontrollersarray e le importazioni, - Rimuovi il riferimento a
AppServiceneiprovidersarray e le importazioni.
Potrebbe anche essere necessario modificare il README.md per soddisfare le vostre specifiche.
Il tuo app.module.ts il file dovrebbe assomigliare a questo,
//app.module.ts
import { Module } from '@nestjs/common';
@Module({
imports: [],
controllers: [],
providers: [],
})
export class AppModule {}
Variabili ambientali
Come buona pratica, alcune informazioni riservate nel codice non dovrebbero essere rese pubbliche. Ad esempio il tuo PORT e il tuo MongoDB URI .
Risolviamo questo problema nel tuo codice.
Sul tuo terminale corri
npm i dotenv
Quindi crea un .env file nella tua directory e aggiungilo al tuo .gitignore file. Memorizza il tuo PORT variabile, dovrai anche memorizzare il tuo MongoDB URI più tardi nello stesso luogo. Ora sostituisci il PORT esposto nel tuo main.ts file. Per fare ciò, importa il dotenv pacchetto e chiama il .config() metodo su di esso.
import * as dotenv from 'dotenv';
dotenv.config();
Questo dovrebbe essere il tuo main.ts file dopo aver seguito i passaggi precedenti.
//main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';
dotenv.config();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT);
}
bootstrap();
Generazione di moduli
Per generare un modulo NestJS utilizzando l'interfaccia a riga di comando NestJS, esegui il frammento di codice riportato di seguito.
nest generate module blogs
Questo comando crea un blogs cartella che contiene un blogs.module.ts file e registra BlogsModule nel tuo app.module.ts file.
Generazione di interfacce
Generiamo un'interfaccia utilizzando NestJS CLI per eseguire il controllo del tipo per l'oggetto che rappresenterà i post del tuo blog. Per raggiungere questo obiettivo devi prima cd nei blogs cartella perché si consiglia di archiviarli vicino agli oggetti di dominio a cui sono associati.
cd src/blogs
Quindi esegui il frammento di codice di seguito per generare l'interfaccia.
nest generate interface blogs
questo crea un blogs.interface.ts file. Qui è dove definiremo la nostra interfaccia. chiameremo l'interfaccia BlogsInterface .
export interface BlogsInterface {
title: string;
body: string;
category: string;
dateCreated: Date;
}
prima di eseguire altri comandi sul tuo terminale, ricorda di cd fuori dal src cartella e di nuovo nella cartella principale eseguendo
cd ../..
Generazione di servizi e controller
Dovrai generare una classe di servizio per archiviare e recuperare i dati e gestire tutta la logica e una classe controller per gestire tutte le richieste in entrata e le risposte in uscita.
Servizio
Per generare un servizio, esegui il comando seguente,
nest generate service blogs
Questo comando crea due file blogs.service.spec.ts e il blogs.service.ts e registra il servizio nei providers array nel blogs.module.ts .
Titolare
Per generare un controller, eseguire il comando seguente,
nest generate controller blogs
Questo comando crea due file blogs.controller.spec.ts e il blogs.controller.ts e registra il controller nei controllers array nel blogs.module.ts .
Con questi la struttura del tuo blog è quasi completa, devi solo creare il BlogsService accessibile ad altre parti del programma. Puoi ottenerlo creando un exports array nel blogs.module.ts file e registrando il BlogsService in quella matrice.
//blogs.module.ts
import { Module } from '@nestjs/common';
import { BlogsService } from './blogs.service';
import { BlogsController } from './blogs.controller';
@Module({
providers: [BlogsService],
controllers: [BlogsController],
exports: [BlogsService],
})
export class BlogsModule {}
MongoDB(Mangusta).
Installa mangusta eseguendo,
npm install --save @nestjs/mongoose mongoose
Dopo l'installazione, importa {MongooseModule} da '@nestjs/mongoose’ nel tuo app.module.ts file. Quindi prendi il tuo MongoDB URI e salvalo nel tuo .env file. Ripeti i passaggi per importare dotenv in app.module.ts file. Quindi nelle imports array chiama .forRoot() metodo che accetta il tuo MongoDB URI come argomento nel MongooseModule . Simile a mongoose.connect() nelle normali app express.
@Module({
imports: [BlogsModule, MongooseModule.forRoot(process.env.MONGODB_URI)],
Creazione di uno schema.
Creiamo uno schema per definire la forma dei blog nella nostra collezione. Per fare questo,
- Crea una cartella all'interno dei tuoi
blogscartella, denominarlaschemas, - All'interno degli
schemascartella, crea un file e chiamaloblogs.schema.ts.
Allora,
In primo luogo, dovrai,
- Importa il
propdecoratore, loSchemadecoratore eSchemaFactoryda@nestjs/mongoose, - Crea una classe
Bloged esportarlo, - Trasforma la classe in uno Schema inserendo
@Schema()decoratore sopra la classe, - Crea un
BlogSchemacostante , assegna il valore di ritorno della chiamata a.createForClass(Blog)con il nome della tua classe come argomento suSchemaFactoryche hai importato in precedenza.
//blogs.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Blog {}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Quindi dovrai definire le proprietà dello Schema.
Per definire una proprietà nello schema dovrai contrassegnare ciascuna di esse con il @prop() decoratore. Il @prop decorator accetta un oggetto options o una dichiarazione di tipo complesso. Le dichiarazioni di tipo complesso potrebbero essere matrici e dichiarazioni di tipi di oggetti nidificati.
//blogs.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
@Schema()
export class Blog {
@Prop({ required: true })
title: string;
@Prop({ required: true })
body: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
dateCreated: Date;
}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Prossima importazione { Document } da 'mongoose' .
Quindi crea un tipo di unione con la classe Schema e il Document importato . Così,
//blogs.schema.ts
import { Document } from 'mongoose';
export type BlogDocument = Blog & Document;
Il tuo blogs.schema.ts finale il file dovrebbe assomigliare a questo,
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
export type BlogDocument = Blog & Document;
@Schema()
export class Blog {
@Prop({ required: true })
title: string;
@Prop({ required: true })
body: string;
@Prop({ required: true })
category: string;
@Prop({ required: true })
dateCreated: Date;
}
export const BlogSchema = SchemaFactory.createForClass(Blog);
Schema di registrazione
Dovrai importare tutto nel tuo blogs.module.ts file. Per raggiungere questo obiettivo dovrai,
- Importa
{MongooseModule}da'@nestjs/mongoose’, - Importa
{Blog, BlogSchema}da'./schemas/blogs.schema’ - Crea un
importsarray all'interno del@moduledecoratore - Chiama il
.forFeature()metodo sulMongooseModule. Questo accetta un array contenente un oggetto che definisce unnamee unoschemaproprietà che dovrebbe essere impostata sul tuoBlog.namee il tuoBlogSchemarispettivamente.
@Module({
imports: [
MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
],
Schema di iniezione
Dovrai inserire il Blog modello nel blogs.service.ts usando il @InjectModel() decoratore. Per raggiungere questo obiettivo dovrai
- importa
{ Model }da'mongoose', - importa
{ InjectModel }da'@nestjs/mongoose’, - Importa
{Blog, BlogDocument}da'./schemas/blogs.schema’, - Crea un
constructorall'interno delBlogsServiceclasse, - Dichiara un
privatevariabile e chiamalablogModele assegna un tipo diModel<BlogDocument>ad esso. Tutti i metodi mangusta verranno chiamati su questa variabile.
Ricordalo, BlogDocument è il tipo di unione del Blog classe e il Model di Mongoose che hai creato in precedenza. Viene utilizzato come tipo generico per la tua variabile.
- Decora
blogModelcon@InjectModel()e passaBlog.namecome argomento.
constructor(
@InjectModel(Blog.name)
private blogModel: Model<BlogDocument>,
) {}
Come funziona il percorso
Ormai avrai notato che il @Controller decorator ha la stringa 'blogs' passato in esso. Ciò significa che il controller invierà tutte le risposte e gestirà tutte le richieste effettuate su https://localhost/3000/blogs .
Successivamente implementerai il servizio e la logica del controller.
Logica di servizio e controller.
È finalmente giunto il momento di implementare la tua funzionalità CRUD.
Prima di iniziare dovrai configurare il controller. Inizia importando alcuni HTTP decoratori di metodi nel tuo controller.
//blogs.controller.ts
import {
Controller,
Body,
Delete,
Get,
Post,
Put,
Param,
} from '@nestjs/common';
Quindi, dovrai importare il servizio e registrarlo in modo da poterlo accedere e importare l'interfaccia per il controllo del tipo.
//blogs.controller.ts
import { BlogsInterface } from './blogs.interface';
import { BlogsService } from './blogs.service';
Per registrare il tuo servizio crea un constructor all'interno del BlogsController classe e dichiarare un private readonly variabile service e impostane il tipo su BlogsService .
constructor(private readonly service: BlogsService) {}
Ora che sei pronto, iniziamo.
Crea
Logica di servizio
Importa { BlogsInterface } da './blogs.interface' e aggiungi un async funzione al BlogsService classe chiamata createBlog , che prenderà un parametro blogs , con il suo tipo come BlogInterface e il suo tipo restituito come Promise con un generico <Blog> genere.
async createBlog(blog: BlogsInterface): Promise<Blog> {
return await new this.blogModel({
...blog,
dateCreated: new Date(),
}).save();
}
Logica del controller
Nel tuo BlogsController classe aggiungi un async funzione alla classe. Chiamalo createBlog e contrassegnalo con il @Post decoratore che lo definisce come POST richiesta.createBlog accetta un parametro blogs , con il suo tipo come BlogInterface . Contrassegna il parametro con @Body decoratore che estrae l'intero body oggetto dal req oggetto e popola il parametro decorato con il valore di body .
@Post()
async createBlog(
@Body()
blog: BlogsInterface,
) {
return await this.service.createBlog(blog);
}
Leggi
Aggiungi due async metodi, uno per restituire un singolo post del blog e il secondo per restituire tutti i post del blog.
Logica di servizio
async getAllBlogs(): Promise<Blog[]> {
return await this.blogModel.find().exec();
}
async getBlog(id: string): Promise<Blog> {
return await this.blogModel.findById(id);
}
Logica del controller
@Get()
async getAllBlogs() {
return await this.service.getAllBlogs();
}
@Get(':id')
async getBlog(@Param('id') id: string) {
return await this.service.getBlog(id);
}
Il async le funzioni sono contrassegnate da @Get decoratore che lo definisce come GET richiesta.
Il secondo async il decoratore della funzione ha un argomento ':id' . Che è ciò che passerai nel @Param decoratore. Il parametro è contrassegnato da @Param('id') che estrae i params proprietà dal req oggetto e popola il parametro decorato con il valore di params .
Aggiorna
Implementiamo la logica per il PUT richiesta.
Logica di servizio
async updateBlog(id: string, body: BlogsInterface): Promise<Blog> {
return await this.blogModel.findByIdAndUpdate(id, body);
}
Logica del controller
@Put(':id')
async updateBlog(
@Param('id')
id: string,
@Body()
blog: BlogsInterface,
) {
return await this.service.updateBlog(id, blog);
}
Il async il secondo parametro della funzione è contrassegnato da @Body() decoratore che estrae l'intero body oggetto dal req oggetto e popola il parametro decorato con il valore di body .
Elimina
Implementiamo la logica per delete richieste.
Logica di servizio
async deleteBlog(id: string): Promise<void> {
return await this.blogModel.findByIdAndDelete(id);
}
La Promise il tipo generico è void perché un Delete richiesta restituisce una promessa vuota.
Logica del controller
@Delete(':id')
async deleteBlog(@Param('id') id: string) {
return await this.service.deleteBlog(id);
}
Testare l'API
Per testare questa API, dovresti utilizzare uno strumento di test API. Per questo articolo, utilizzerò un popolare strumento di test API chiamato Postman. Userò dati casuali su argomenti popolari da testare.
Crea
Crea un POST richiesta a https://localhost/3000/blogs con i seguenti oggetti JSON, questo aggiungerà tutti i dati al tuo database.
{
"title": "jeen-yuhs",
"body": "The life of superstar rapper Kanye West is currently streaming on Netflix - and according to our jeen-yuhs review, it's a fascinating watch. -credit:Radio Times",
"category":"Music"
}
{
"title": "Why You Should Always Wash Your Hands",
"body": "Germs from unwashed hands can be transferred to other objects, like handrails, tabletops, or toys, and then transferred to another person's hands.-credit cdc.gov",
"category":"Health"
}
{
"title": "Why You Should Follow me on Twitter",
"body": "Well, Because I asked nicely",
"category":"Random"
}
Dovresti ottenere un 201 risposta e il blog creato con una data e un _id aggiunto.
Leggi
Crea un GET richiesta a https://localhost/3000/blogs . Questo dovrebbe restituire un
200 risposta con una matrice di tutti i dati che hai aggiunto in precedenza. Copia il _id proprietà di uno degli oggetti dell'array.
Crea un altro GET richiesta a https://localhost/3000/blogs/id con l'ID precedentemente copiato. Questo dovrebbe restituire un 200 risposta con i dati dell'oggetto il cui id è stato utilizzato per effettuare la richiesta.
Aggiorna
Crea un PUT richiesta a https://localhost/3000/blogs/id con i dati sottostanti. Il id dovrebbe essere sostituito con quello che hai copiato in precedenza. Questo dovrebbe restituire un 200 risposta e aggiorna l'oggetto con l'id dietro le quinte. se esegui un altro GET richiesta dovresti ottenere l'oggetto aggiornato.
{
"title": "why you Should Cut your Nails",
"body": "It's important to trim your nails regularly. Nail trimming together with manicures makes your nails look well-groomed, neat, and tidy.- credit:WebMD",
"category":"Health"
}
Elimina
Crea un DELETE richiesta a https://localhost/3000/blogs/id .Questo dovrebbe restituire un 200 risposta ed elimina l'oggetto con l'id dietro le quinte. se esegui un altro GET richiesta non vedrai l'oggetto eliminato.
Conclusione
Quindi siamo finalmente alla fine di questo articolo. Ricapitoliamo ciò che hai trattato.
- Cos'è NestJS
- Terminologie in NestJS,
- Creazione di un'app NestJS,
- Integrazione di MongoDB in un'app NestJS,
- App di manipolazione e NestJS
È parecchio, congratulazioni per essere arrivato così lontano.
Puoi trovare il codice su github.
Buona fortuna per il tuo viaggio NestJS!