MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

O ABC do NestJS:um guia para iniciantes com MongoDB (Mongoose).

O que é NestJS?


NestJS é um framework NodeJS moderno que faz uso de frameworks NodeJS populares como Express e Fastify sob o capô. O NestJS foi amplamente inspirado no Angular e, como resultado, emprega um sistema de módulos no estilo Angular. NestJS é escrito em TypeScript, embora também suporte JavaScript nativo.

Pré-requisitos


Para seguir este tutorial, você deve atender aos seguintes requisitos
  • Competência em PostMan ou qualquer outra ferramenta de teste de API.
  • Conhecimento básico de aplicativos NodeJS e Express.
  • Conhecimento básico de TypeScript.
  • Competência em MongoDB (Mongoose).

O seguinte deve ser instalado em seu sistema
  • NodeJS v.14 e superior.
  • Código do Visual Studio (recomendado) ou qualquer outro IDE.
  • PostMan ou qualquer outra ferramenta de teste de API.

Terminologias comuns usadas no NestJS;


Aqui estão alguns dos termos mais usados ​​no NestJS que você encontrará muito neste artigo.

Interfaces


Uma interface é uma definição de tipo. Como resultado, ele é utilizado como verificador/executor de tipos em funções, classes, etc.

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

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

A humanInterface acima executa uma verificação de tipo estrita no kevin objeto. O Typescript geraria um erro se você adicionasse outro campo ou alterasse o tipo de qualquer uma das propriedades do objeto.

Controladores


Os controladores são responsáveis ​​por receber as solicitações recebidas e responder ao cliente. Um controlador colabora com seu serviço associado.

Serviços


Um serviço é um provedor que armazena e recupera dados e é usado com seu controlador correspondente.

Decoradores


Um decorador é uma expressão de retorno de função que aceita um target , name , e property descriptor como argumentos opcionais. Decoradores são escritos como @decorator-name . Eles geralmente são anexados a declarações de classe, métodos e parâmetros.

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

O @Get decorador acima marca o bloco de código abaixo dele como um GET solicitação. Mais sobre isso mais tarde.

Módulo


Um módulo é uma parte de um programa que lida com uma tarefa específica. Um módulo no NestJS é marcado anotando uma classe anotada com o @Module() decorador. Nest usa os metadados fornecidos pelo @Module() decorador para organizar a estrutura do aplicativo.

Instalando a CLI


Para começar, você precisará instalar o NestJS CLI ****com npm . Você pode pular esta etapa se já tiver o NestJS CLI instalado em seu sistema.

npm i -g @nestjs/cli

Este bloco de código acima instalará a CLI Nest globalmente em seu sistema.

Criando um novo projeto


Para gerar um novo projeto, execute nest new seguido pelo nome do projeto desejado. Para este artigo, escreveremos uma API de blog simples com funcionalidade CRUD enquanto aderimos aos padrões RESTful.

nest new Blog-Api

Este comando solicitará que você selecione um gerenciador de pacotes, escolha npm .

Isso irá, então, estruturar toda a estrutura do projeto com um endpoint de API de teste cuja porta está definida como 3000 por padrão. Você pode testá-lo em http://localhost:3000 depois de executar o npm run start:dev comando que iniciará o servidor no modo de observação semelhante ao que o nodemon faz em aplicativos expressos.

Depois de testar o endpoint, você precisará excluir alguns dos arquivos padrão porque não precisará mais deles. Para fazer isso;
  • abra a pasta src e dentro dela,
  • excluir app.controller.spec.ts ,
  • excluir app.controller.ts ,
  • excluir app.service.ts ,
  • Abra app.module.ts ,
  • Remova a referência a AppController nos controllers array e as importações,
  • Remova a referência a AppService nos providers array e as importações.

Você também pode precisar alterar o README.md para atender às suas especificações.

Seu app.module.ts arquivo deve ficar assim,

//app.module.ts

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

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

Variáveis ​​Ambientais


Como boa prática, algumas informações confidenciais em seu código não devem ser tornadas públicas. Por exemplo, seu PORT e seu MongoDB URI .

Vamos corrigir isso no seu código.

No seu terminal, execute

npm i dotenv

Em seguida, crie um .env arquivo em seu diretório e adicione-o ao seu .gitignore Arquivo. Armazene seu PORT variável, você também terá que armazenar seu MongoDB URI depois no mesmo lugar. Agora substitua o PORT exposto em seu main.ts Arquivo. Para fazer isso, importe o dotenv pacote e chame o .config() método nele.

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

Este deve ser seu main.ts arquivo depois de seguir as etapas acima.

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

Gerando Módulos


Para gerar um módulo NestJS usando a CLI NestJS, execute o trecho de código abaixo.

nest generate module blogs

Este comando cria um blogs pasta que contém um blogs.module.ts arquivo e registra BlogsModule em seu app.module.ts Arquivo.

Gerando interfaces


Vamos gerar uma interface usando a CLI do NestJS para fazer a verificação de tipo do objeto que representará as postagens do seu blog. Para conseguir isso, você deve primeiro cd nos blogs porque é recomendado que sejam armazenados próximos aos objetos de domínio aos quais estão associados.

cd src/blogs

Em seguida, execute o trecho de código abaixo para gerar a interface.

nest generate interface blogs

isso cria um blogs.interface.ts Arquivo. É aqui que vamos definir nossa interface. vamos nomear a interface BlogsInterface .

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

antes de executar mais comandos em seu terminal, lembre-se de cd fora do src pasta e de volta para sua pasta raiz executando

cd ../..

Gerando serviços e controladores


Você precisará gerar uma classe de serviço para armazenar e recuperar dados e lidar com toda a lógica e uma classe de controlador para lidar com todas as solicitações de entrada e respostas de saída.

Serviço


Para gerar um serviço execute o comando abaixo,

nest generate service blogs

Este comando cria dois arquivos blogs.service.spec.ts e o blogs.service.ts e registra o serviço nos providers matriz no blogs.module.ts .

Controlador


Para gerar um controlador execute o comando abaixo,

nest generate controller blogs

Este comando cria dois arquivos o blogs.controller.spec.ts e o blogs.controller.ts e registra o controlador em controllers matriz no blogs.module.ts .

Com isso sua estrutura de blogs está quase completa, você só precisa fazer o BlogsService acessível a outras partes do seu programa. Você pode conseguir isso criando um exports matriz no blogs.module.ts arquivo e registrando o BlogsService nessa matriz.

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


Instale o mangusto executando,

npm install --save @nestjs/mongoose mongoose

Após a instalação, importe {MongooseModule} de '@nestjs/mongoose’ em seu app.module.ts Arquivo. Em seguida, pegue seu MongoDB URI e armazene-o em seu .env Arquivo. Repita as etapas para importar dotenv no app.module.ts Arquivo. Em seguida, nas imports array chama o .forRoot() método que leva seu MongoDB URI como um argumento no MongooseModule . Semelhante ao mongoose.connect() em aplicativos expressos regulares.

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

Criando um esquema.


Vamos criar um esquema para definir a forma dos blogs em nossa coleção. Para fazer isso,
  • Crie uma pasta dentro de seus blogs pasta, nomeie-a schemas ,
  • Dentro dos schemas pasta, crie um arquivo e chame-o de blogs.schema.ts .

Então,

Em primeiro lugar, você terá que,
  • Importar o prop decorador, o Schema decorador e o SchemaFactory de @nestjs/mongoose ,
  • Criar uma classe Blog e exportá-lo,
  • Transforme a classe em um Schema colocando o @Schema() decorador acima da classe,
  • Criar uma constante BlogSchema , atribua o valor de retorno de chamar o .createForClass(Blog) com o nome de sua classe como argumento em SchemaFactory que você importou anteriormente.
//blogs.schema.ts

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

@Schema()
export class Blog {}

export const BlogSchema = SchemaFactory.createForClass(Blog);

Em seguida, você precisará definir as propriedades do Schema.

Para definir uma propriedade no esquema, você precisará marcar cada uma delas com o @prop() decorador. O @prop decorador aceita um objeto de opções ou uma declaração de tipo complexo. As declarações de tipo complexo podem ser arrays e declarações de tipo de objeto aninhado.

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

Próxima importação { Document } de 'mongoose' .

Em seguida, crie um tipo de união com a classe Schema e o Document importado . Igual a,

//blogs.schema.ts

import { Document } from 'mongoose';

export type BlogDocument = Blog & Document;

Seu blogs.schema.ts final arquivo deve ficar assim,

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

Esquema de registro


Você precisará importar tudo para o seu blogs.module.ts Arquivo. Para conseguir isso, você precisará,
  • Importar {MongooseModule} de '@nestjs/mongoose’ ,
  • Importar {Blog, BlogSchema} de './schemas/blogs.schema’
  • Crie uma imports array dentro do @module decorador
  • Chame o .forFeature() método no MongooseModule . Isso recebe um array contendo um objeto que define um name e um schema propriedade que deve ser definida para seu Blog.name e seu BlogSchema respectivamente.
@Module({
  imports: [
    MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
  ],

Injetando Esquema


Você precisará injetar o Blog model no blogs.service.ts usando o @InjectModel() decorador. Para conseguir isso você terá que
  • importar { Model } de 'mongoose' ,
  • importar { InjectModel } de '@nestjs/mongoose’ ,
  • Importar {Blog, BlogDocument} de './schemas/blogs.schema’ ,
  • Crie um constructor dentro do BlogsService classe,
  • Declarar um private variável e chame-a de blogModel e atribua um tipo de Model<BlogDocument> para isso. Todos os métodos do mangusto serão chamados nesta variável.

Lembre-se disso, BlogDocument é o tipo de união do Blog class e o Model do Mongoose que você criou anteriormente. Ele é usado como o tipo genérico para sua variável.
  • Decore blogModel com @InjectModel() e passe Blog.name como argumento.
constructor(
    @InjectModel(Blog.name)
    private blogModel: Model<BlogDocument>,
  ) {}

Como funciona o roteamento


Até agora você deve ter notado que o @Controller decorador tem a string 'blogs' passou nele. Isso significa que o controlador enviará todas as respostas e tratará de todas as solicitações feitas em http://localhost/3000/blogs .

Em seguida, você implementará o serviço e a lógica do controlador.

Lógica de serviço e controlador.


Finalmente chegou a hora de implementar sua funcionalidade CRUD.

Antes de começarmos, você precisará configurar seu controlador. Comece importando alguns HTTP decoradores de método em seu controlador.

//blogs.controller.ts

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

Em seguida, você precisará importar o serviço e registrá-lo para poder acessá-lo e importar a interface para verificação de tipo.

//blogs.controller.ts

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

Para registrar seu serviço crie um constructor dentro do BlogsController class e declare um private readonly variável service e defina seu tipo como BlogsService .

constructor(private readonly service: BlogsService) {}

Agora que você está tudo configurado, vamos começar.

Criar


Lógica de serviço

Importar { BlogsInterface } de './blogs.interface' e adicione um async função para o BlogsService classe chamada createBlog , que terá um parâmetro blog , com seu tipo como BlogInterface , e seu tipo de retorno como uma Promise com um <Blog> genérico tipo.

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

Lógica do controlador

Em seu BlogsController class adiciona um async função para a classe. Chame de createBlog e marque-o com o @Post decorador que o define como um POST request.createBlog leva um parâmetro blog , com seu tipo como BlogInterface . Marque o parâmetro com @Body decorador que extrai todo o body objeto do req objeto e preenche o parâmetro decorado com o valor de body .

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

Leia


Adicione dois async métodos, um para retornar uma única postagem do blog e o segundo para retornar todas as postagens do blog.

Lógica de serviço

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

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

Lógica do controlador

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

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

O async funções são marcadas com o @Get decorador que o define como um GET solicitação.

O segundo async o decorador da função tem um argumento ':id' . Que é o que você passará para o @Param decorador. O parâmetro é marcado com o @Param('id') que extrai os params propriedade da req objeto e preenche o parâmetro decorado com o valor de params .

Atualizar


Vamos implementar a lógica para o PUT solicitação.

Lógica de serviço

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

Lógica do controlador

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

O async o segundo parâmetro da função é marcado com o @Body() decorador que extrai todo o body objeto do req objeto e preenche o parâmetro decorado com o valor de body .

Excluir


Vamos implementar a lógica para delete solicitações de.

Lógica de serviço

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

A Promise o tipo genérico é void porque um Delete request retorna uma promessa vazia.

Lógica do controlador

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

Testando a API


Para testar esta API, você deve usar uma ferramenta de teste de API. Para este artigo, usarei uma ferramenta de teste de API popular chamada Postman. Usarei dados aleatórios sobre tópicos populares para testar.

Criar


Faça um POST solicitação para http://localhost/3000/blogs com os seguintes objetos JSON, isso adicionará todos os dados ao seu banco de dados.

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

Você deve obter um 201 resposta e o blog criado com uma data e um _id adicionado.

Leia


Faça um GET solicitação para http://localhost/3000/blogs . Isso deve retornar um

200 resposta com uma matriz de todos os dados que você adicionou anteriormente. Copie o _id propriedade de um dos objetos da matriz.

Faça outro GET solicitação para http://localhost/3000/blogs/id com o ID copiado anteriormente. Isso deve retornar um 200 resposta com os dados do objeto cujo id foi usado para fazer a solicitação.

Atualizar


Faça um PUT solicitação para http://localhost/3000/blogs/id com os dados abaixo. O id deve ser substituído pelo que você copiou anteriormente. Isso deve retornar um 200 resposta e atualiza o objeto com o id Por trás das cenas. se você executar outro GET request, você deve obter o objeto atualizado.

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

Excluir


Faça um DELETE solicitação para http://localhost/3000/blogs/id .Isso deve retornar um 200 resposta e exclui o objeto com o id Por trás das cenas. se você executar outro GET solicitação, você não verá o objeto excluído.

Conclusão


Então, finalmente chegamos ao final deste artigo. Vamos recapitular o que você cobriu.
  • O que é NestJS,
  • Terminologias no NestJS,
  • Criando um aplicativo NestJS,
  • Integrando o MongoDB em um aplicativo NestJS,
  • Aplicativo de manipulação e NestJS,

Isso é muito, parabéns por chegar até aqui.

Você pode encontrar o código no github.

Boa sorte em sua jornada NestJS!