sql >> Databasteknik >  >> NoSQL >> MongoDB

The ABCs of NestJS:En nybörjarguide med MongoDB(Mongoose).

Vad är NestJS?

NestJS är ett modernt NodeJS-ramverk som använder sig av populära NodeJS-ramverk som Express och Fastify under huven. NestJS var till stor del inspirerad av Angular, och som ett resultat använder det ett modulsystem i Angular-stil. NestJS är skrivet i TypeScript, även om det också stöder inbyggt JavaScript.

Förutsättningar

För att följa denna handledning måste du uppfylla följande krav

  • Kompetens i PostMan eller något annat API-testverktyg.
  • Grundläggande kunskaper om NodeJS- och Express-appar.
  • Grundläggande kunskaper om TypeScript.
  • Kompetens i MongoDB(Mongoose).

Följande bör installeras på ditt system

  • NodeJS v.14 och högre.
  • Visual Studio Code (rekommenderas) eller någon annan IDE.
  • PostMan eller något annat API-testverktyg.

Vanliga terminologier som används i NestJS;

Här är några av de mest använda termerna i NestJS som du kommer att stöta på mycket i den här artikeln.

Gränssnitt

Ett gränssnitt är en typdefinition. Som ett resultat används den som en typkontroll/enforcer i funktioner, klasser etc.

interface humanInterface{
  name:string;
  gender:string;
  age:number;
}

const kevin: humanInterface={
  name:'Kevin Sunders',
  gender:'Male',
  age: 25,
}

humanInterface ovan utför strikt typkontroll på kevin objekt. Typescript skulle ge ett felmeddelande om du lade till ett annat fält eller ändrade typen av någon av objektegenskaperna.

Styrenheter

Controllers ansvarar för att ta emot inkommande förfrågningar och svara på klienten. En styrenhet samarbetar med sin tillhörande tjänst.

Tjänster

En tjänst är en leverantör som lagrar och hämtar data och som används med dess motsvarande kontrollant.

Dekoratörer

En dekoratör är ett funktionsreturnerande uttryck som accepterar ett target , name och property descriptor som valfria argument. Dekoratörer skrivs som @decorator-name . De är vanligtvis kopplade till klassdeklarationer, metoder och parametrar.

@Get()
   getAll(): Model[] {
    return this.testService.getAll();
  }

@Get decorator ovan markerar kodblocket under det som en GET begäran. Mer om det senare.

Modul

En modul är en del av ett program som hanterar en viss uppgift. En modul i NestJS markeras genom att kommentera en klass som är kommenterad med @Module() dekoratör. Nest använder metadata som tillhandahålls av @Module() dekoratör för att organisera applikationsstrukturen.

Installera CLI

För att komma igång måste du installera NestJS CLI ****med npm . Du kan hoppa över det här steget om du redan har NestJS CLI installerat på ditt system.

npm i -g @nestjs/cli

Detta kodblock ovan kommer att installera Nest CLI globalt på ditt system.

Skapa ett nytt projekt

För att skapa ett nytt projekt kör nest new följt av ditt önskade projektnamn. För den här artikeln kommer vi att skriva ett enkelt blogg-API med CRUD-funktionalitet samtidigt som vi följer RESTful-standarderna.

nest new Blog-Api

Detta kommando kommer att uppmana dig att välja en pakethanterare, välj npm .

Detta kommer sedan att bygga hela projektstrukturen med en test-API-slutpunkt vars port är inställd på 3000 som standard. Du kan testa det på http://localhost:3000 efter att ha kört npm run start:dev kommando som startar servern i bevakningsläge liknande vad nodemon gör i expressappar.

Efter att ha testat slutpunkten måste du ta bort några av standardfilerna eftersom du inte kommer att behöva dem längre. För att göra detta;

  • öppna src-mappen och inuti,
  • ta bort app.controller.spec.ts ,
  • ta bort app.controller.ts ,
  • ta bort app.service.ts ,
  • Öppna app.module.ts ,
  • Ta bort referensen till AppController i controllers array och importerna,
  • Ta bort referensen till AppService i providers array och importerna.

Du kan också behöva ändra README.md för att uppfylla dina specifikationer.

Din app.module.ts filen ska se ut så här,

//app.module.ts

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

@Module({
  imports: [],
  controllers: [],
  providers: [],
})
export class AppModule {}

Miljövariabler

Som god praxis bör viss känslig information i din kod inte offentliggöras. Till exempel din PORT och din MongoDB URI .

Låt oss fixa detta i din kod.

Kör på din terminal

npm i dotenv

Skapa sedan en .env filen i din katalog och lägg till den i din .gitignore fil. Lagra din PORT variabel måste du också lagra din MongoDB URI senare på samma plats. Byt nu ut den exponerade PORT i din main.ts fil. För att göra detta, importera dotenv paketera och anropa .config() metod på det.

import * as dotenv from 'dotenv';
dotenv.config();

Detta bör vara din main.ts fil efter att du följt stegen ovan.

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

Generera moduler

För att generera en NestJS-modul med NestJS CLI, kör kodavsnittet nedan.

nest generate module blogs

Detta kommando skapar en blogs mapp som innehåller en blogs.module.ts fil och registrerar BlogsModule i din app.module.ts fil.

Generera gränssnitt

Låt oss skapa ett gränssnitt med NestJS CLI för att göra typkontrollen för objektet som kommer att representera dina blogginlägg. För att uppnå detta måste du först cd i blogs mapp eftersom det rekommenderas att de lagras nära domänobjekten som de är kopplade till.

cd src/blogs

Kör sedan kodavsnittet nedan för att generera gränssnittet.

nest generate interface blogs

detta skapar en blogs.interface.ts fil. Det är här vi kommer att definiera vårt gränssnitt. vi kommer att döpa gränssnittet till BlogsInterface .

export interface BlogsInterface {
  title: string;
  body: string;
  category: string;
  dateCreated: Date;
}

innan du kör några fler kommandon på din terminal, kom ihåg att cd ur src mapp och tillbaka till din rotmapp genom att köra

cd ../..

Generera tjänster och kontroller

Du måste skapa en serviceklass för att lagra och hämta data och hantera all logik och en kontrollklass för att hantera alla inkommande förfrågningar och utgående svar.

Tjänst

För att generera en tjänst kör kommandot nedan,

nest generate service blogs

Detta kommando skapar två filer blogs.service.spec.ts och blogs.service.ts och registrerar tjänsten i providers array i blogs.module.ts .

Styrenhet

För att generera en styrenhet kör kommandot nedan,

nest generate controller blogs

Detta kommando skapar två filer blogs.controller.spec.ts och blogs.controller.ts och registrerar styrenheten i controllers array i blogs.module.ts .

Med dessa är din bloggstruktur nästan komplett, du behöver bara göra BlogsService tillgänglig för andra delar av ditt program. Du kan uppnå detta genom att skapa en exports array i blogs.module.ts fil och registrera BlogsService i den uppsättningen.

//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(Mongos).

Installera mongoose genom att köra,

npm install --save @nestjs/mongoose mongoose

Efter installationen, importera {MongooseModule} från '@nestjs/mongoose’ till din app.module.ts fil. Ta sedan din MongoDB URI och lagra den i din .env fil. Upprepa stegen för att importera dotenv i app.module.ts fil. Sedan i imports array anropar .forRoot() metod som tar din MongoDB URI som ett argument på MongooseModule . Liknar mongoose.connect() i vanliga expressappar.

@Module({
  imports: [BlogsModule, MongooseModule.forRoot(process.env.MONGODB_URI)],

Skapa ett schema.

Låt oss skapa ett schema för att definiera formen på bloggarna i vår samling. För att göra detta,

  • Skapa en mapp i dina blogs mapp, döp den till schemas ,
  • Inuti schemas mapp, skapa en fil och kalla den blogs.schema.ts .

Sedan,

För det första måste du,

  • Importera prop dekoratör, Schema decorator och SchemaFactory från @nestjs/mongoose ,
  • Skapa en klass blogs och exportera den,
  • Förvandla klassen till ett schema genom att placera @Schema() dekoratör ovanför klassen,
  • Skapa ett konstant BlogSchema , tilldela returvärdet för att anropa .createForClass(Blog) med namnet på din klass som argument på SchemaFactory som du importerade tidigare.
//blogs.schema.ts

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';

@Schema()
export class Blog {}

export const BlogSchema = SchemaFactory.createForClass(Blog);

Sedan måste du definiera egenskaperna för schemat.

För att definiera en egenskap i schemat måste du markera var och en av dem med @prop() dekoratör. @prop decorator accepterar ett alternativobjekt eller en komplex typdeklaration. De komplexa typdeklarationerna kan vara arrayer och kapslade objekttypsdeklarationer.

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

Nästa import { Document } från 'mongoose' .

Skapa sedan en fackföreningstyp med Schema-klassen och det importerade Document . Såhär,

//blogs.schema.ts

import { Document } from 'mongoose';

export type BlogDocument = Blog & Document;

Din sista blogs.schema.ts filen ska se ut så här,

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

Registreringsschema

Du måste importera allt till din blogs.module.ts fil. För att uppnå detta måste du,

  • Importera {MongooseModule} från '@nestjs/mongoose’ ,
  • Importera {Blog, BlogSchema} från './schemas/blogs.schema’
  • Skapa en imports array inuti @module dekoratör
  • Anropa .forFeature() metod på MongooseModule . Detta tar in en array som innehåller ett objekt som definierar ett name och ett schema egenskap som ska ställas in på ditt Blog.name och ditt BlogSchema respektive.
@Module({
  imports: [
    MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
  ],

Injektionsschema

Du måste injicera Blog modell till blogs.service.ts med @InjectModel() dekoratör. För att uppnå detta måste du

  • importera { Model } från 'mongoose' ,
  • importera { InjectModel } från '@nestjs/mongoose’ ,
  • Importera {Blog, BlogDocument} från './schemas/blogs.schema’ ,
  • Skapa en constructor inuti BlogsService klass,
  • Deklarera en private variabel och kalla den blogModel och tilldela en typ av Model<BlogDocument> till det. Alla mongoose-metoder kommer att anropas på denna variabel.

Kom ihåg det, BlogDocument är fackföreningstypen för Blog klass och Mongoose Model som du skapade tidigare. Den används som generisk typ för din variabel.

  • Dekorera blogModel med @InjectModel() och skicka Blog.name som argument.
constructor(
    @InjectModel(Blog.name)
    private blogModel: Model<BlogDocument>,
  ) {}

Hur routing fungerar

Vid det här laget måste du ha märkt att @Controller decorator har strängen 'blogs' gått in i det. Detta innebär att kontrollern kommer att skicka alla svar och hantera alla förfrågningar som görs på http://localhost/3000/blogs .

Nästa upp kommer du att implementera tjänsten och styrenhetens logik.

Service- och kontrolllogik.

Det är äntligen dags att implementera din CRUD-funktionalitet.

Innan vi börjar måste du konfigurera din kontroller. Börja med att importera lite HTTP metoddekoratörer i din handkontroll.

//blogs.controller.ts

import {
  Controller,
  Body,
  Delete,
  Get,
  Post,
  Put,
  Param,
} from '@nestjs/common';

Sedan måste du importera tjänsten och registrera den så att du kan komma åt den och importera gränssnittet för typkontroll.

//blogs.controller.ts

import { BlogsInterface } from './blogs.interface';
import { BlogsService } from './blogs.service';

Skapa en constructor för att registrera din tjänst inuti BlogsController klass och deklarera en private readonly variabel service och ställ in dess typ till BlogsService .

constructor(private readonly service: BlogsService) {}

Nu när du är klar, låt oss komma igång.

Skapa

Servicelogik

Importera { BlogsInterface } från './blogs.interface' och lägg till en async funktion till BlogsService klass som heter createBlog , som tar en parameter blogs , med dess typ som BlogInterface , och dess returtyp som ett Promise med en generisk <Blog> typ.

async createBlog(blog: BlogsInterface): Promise<Blog> {
    return await new this.blogModel({
      ...blog,
      dateCreated: new Date(),
    }).save();
  }

Controller Logic

I din BlogsController klass lägga till en async funktion till klassen. Kalla det createBlog och markera den med @Post dekorator som definierar det som ett POST begäran.createBlog tar en parameter blogs , med dess typ som BlogInterface . Markera parametern med @Body dekoratör som extraherar hela body objekt från req objekt och fyller i den dekorerade parametern med värdet body .

@Post()
  async createBlog(
    @Body()
    blog: BlogsInterface,
  ) {
    return await this.service.createBlog(blog);
  }

Läs

Lägg till två async metoder, en för att returnera ett enda blogginlägg och den andra för att returnera alla blogginlägg.

Servicelogik

async getAllBlogs(): Promise<Blog[]> {
    return await this.blogModel.find().exec();
  }

  async getBlog(id: string): Promise<Blog> {
    return await this.blogModel.findById(id);
  }

Controller Logic

  @Get()
  async getAllBlogs() {
    return await this.service.getAllBlogs();
  }

  @Get(':id')
  async getBlog(@Param('id') id: string) {
    return await this.service.getBlog(id);
  }

async funktioner är markerade med @Get dekorator som definierar det som en GET begäran.

Den andra async funktionens dekorator har ett argument ':id' . Vilket är vad du skickar till @Param dekoratör. Parametern är markerad med @Param('id') som extraherar params egenskap från req objekt och fyller i den dekorerade parametern med värdet för params .

Uppdatera

Låt oss implementera logiken för PUT begäran.

Servicelogik

async updateBlog(id: string, body: BlogsInterface): Promise<Blog> {
    return await this.blogModel.findByIdAndUpdate(id, body);
  }

Controller Logic

@Put(':id')
  async updateBlog(
    @Param('id')
    id: string,
    @Body()
    blog: BlogsInterface,
  ) {
    return await this.service.updateBlog(id, blog);
  } 

async funktions andra parameter är markerad med @Body() dekoratör som extraherar hela body objekt från req objekt och fyller i den dekorerade parametern med värdet body .

Ta bort

Låt oss implementera logiken för delete förfrågningar.

Servicelogik

async deleteBlog(id: string): Promise<void> {
    return await this.blogModel.findByIdAndDelete(id);
  }

Promise generisk typ är void eftersom en Delete begäran returnerar ett tomt löfte.

Controller Logic

@Delete(':id')
  async deleteBlog(@Param('id') id: string) {
    return await this.service.deleteBlog(id);
  }

Testar API

För att testa detta API bör du använda ett API-testverktyg. För den här artikeln kommer jag att använda ett populärt API-testverktyg som heter Postman. Jag kommer att använda slumpmässig information om populära ämnen för att testa.

Skapa

Gör ett POST begäran till http://localhost/3000/blogs med följande JSON-objekt kommer detta att lägga till all data till din databas.

{
  "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"
}

Du bör få en 201 svar och den skapade bloggen med ett datum och ett _id lagt till.

Läs

Gör en GET begäran till http://localhost/3000/blogs . Detta bör returnera en

200 svar med en uppsättning av all data du tidigare lagt till. Kopiera _id egenskap hos ett av arrayobjekten.

Gör en annan GET begäran till http://localhost/3000/blogs/id med det tidigare kopierade id. Detta bör returnera en 200 svar med data för objektet vars id användes för att göra begäran.

Uppdatera

Gör en PUT begäran till http://localhost/3000/blogs/id med uppgifterna nedan. id bör ersättas med den du kopierade tidigare. Detta bör returnera en 200 svar och uppdaterar objektet som bär id bakom kulisserna. om du kör en annan GET begäran bör du få det uppdaterade objektet.

{
  "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"
}

Ta bort

Gör en DELETE begäran till http://localhost/3000/blogs/id .Detta bör returnera en 200 svar och tar bort objektet som bär id bakom kulisserna. om du kör en annan GET begäran kommer du inte att se det borttagna objektet.

Slutsats

Så vi är äntligen i slutet av den här artikeln. Låt oss sammanfatta vad du har täckt.

  • Vad NestJS är,
  • Terminologier i NestJS,
  • Skapa en NestJS-app,
  • Integrera MongoDB i en NestJS-app,
  • Manipulation och NestJS-app,

Det är ganska mycket, grattis till att du har kommit så långt.

Du kan hitta koden på github.

Lycka till på din NestJS-resa!


  1. Skip/Mock Redis In Junit

  2. Fel:getaddriinfo ENOTFOUND i nodejs for get call

  3. MongoDB $sinh

  4. mongod HostnameCanonicalizationWorker-fel på OS X