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 http://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
AppController
neicontrollers
array e le importazioni, - Rimuovi il riferimento a
AppService
neiproviders
array 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
blogs
cartella, denominarlaschemas
, - All'interno degli
schemas
cartella, crea un file e chiamaloblogs.schema.ts
.
Allora,
In primo luogo, dovrai,
- Importa il
prop
decoratore, loSchema
decoratore eSchemaFactory
da@nestjs/mongoose
, - Crea una classe
Blog
ed esportarlo, - Trasforma la classe in uno Schema inserendo
@Schema()
decoratore sopra la classe, - Crea un
BlogSchema
costante , assegna il valore di ritorno della chiamata a.createForClass(Blog)
con il nome della tua classe come argomento suSchemaFactory
che 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
imports
array all'interno del@module
decoratore - Chiama il
.forFeature()
metodo sulMongooseModule
. Questo accetta un array contenente un oggetto che definisce unname
e unoschema
proprietà che dovrebbe essere impostata sul tuoBlog.name
e il tuoBlogSchema
rispettivamente.
@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
constructor
all'interno delBlogsService
classe, - Dichiara un
private
variabile e chiamalablogModel
e 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
blogModel
con@InjectModel()
e passaBlog.name
come 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 http://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 http://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 http://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 http://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 http://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 http://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!