sql >> Base de Datos >  >> NoSQL >> MongoDB

El ABC de NestJS:una guía para principiantes con MongoDB (Mongoose).

¿Qué es NestJS?

NestJS es un marco moderno de NodeJS que hace uso de marcos populares de NodeJS como Express y Fastify bajo el capó. NestJS se inspiró en gran medida en Angular y, como resultado, emplea un sistema de módulos de estilo Angular. NestJS está escrito en TypeScript, aunque también es compatible con JavaScript nativo.

Requisitos previos

Para seguir este tutorial, debe cumplir con los siguientes requisitos

  • Competencia en PostMan o cualquier otra herramienta de prueba de API.
  • Conocimiento básico de NodeJS y aplicaciones Express.
  • Conocimientos básicos de TypeScript.
  • Competencia en MongoDB(Mongoose).

Lo siguiente debe estar instalado en su sistema

  • NodeJS v.14 y superior.
  • Código de Visual Studio (recomendado) o cualquier otro IDE.
  • PostMan o cualquier otra herramienta de prueba de API.

Terminologías comunes utilizadas en NestJS;

Estos son algunos de los términos más utilizados en NestJS que encontrará mucho en este artículo.

Interfaces

Una interfaz es una definición de tipo. Como resultado, se utiliza como verificador/ejecutor de tipos en funciones, clases, etc.

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

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

La humanInterface anterior realiza una verificación de tipo estricta en el kevin objeto. Typescript generaría un error si agregara otro campo o cambiara el tipo de cualquiera de las propiedades del objeto.

Controladores

Los controladores están a cargo de recibir las solicitudes entrantes y responder al cliente. Un controlador colabora con su servicio asociado.

Servicios

Un servicio es un proveedor que almacena y recupera datos y se utiliza con su correspondiente controlador.

Decoradores

Un decorador es una expresión que devuelve una función que acepta un target , name y property descriptor como argumentos opcionales. Los decoradores se escriben como @decorator-name . Por lo general, se adjuntan a declaraciones de clase, métodos y parámetros.

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

El @Get el decorador de arriba marca el bloque de código debajo de él como GET petición. Más sobre eso más adelante.

Módulo

Un módulo es una parte de un programa que maneja una tarea en particular. Un módulo en NestJS se marca anotando una clase anotada con @Module() decorador. Nest utiliza los metadatos proporcionados por @Module() decorador para organizar la estructura de la aplicación.

Instalación de la CLI

Para comenzar, deberá instalar NestJS CLI **** con npm . Puede omitir este paso si ya tiene la CLI de NestJS instalada en su sistema.

npm i -g @nestjs/cli

Este bloque de código anterior instalará la CLI de nest globalmente en su sistema.

Creando un nuevo proyecto

Para generar un nuevo proyecto, ejecute nest new seguido del nombre de su proyecto deseado. Para este artículo, escribiremos una API de blog simple con funcionalidad CRUD mientras nos adherimos a los estándares RESTful.

nest new Blog-Api

Este comando le pedirá que seleccione un administrador de paquetes, elija npm .

Luego, esto creará un andamiaje en toda la estructura del proyecto con un punto final de la API de prueba cuyo puerto se establece en 3000 por defecto. Puedes probarlo en http://localhost:3000 después de ejecutar npm run start:dev comando que iniciará el servidor en modo de observación similar a lo que hace nodemon en las aplicaciones express.

Después de probar el punto final, deberá eliminar algunos de los archivos predeterminados porque ya no los necesitará. Para hacer esto;

  • abre la carpeta src y dentro,
  • borrar app.controller.spec.ts ,
  • eliminar app.controller.ts ,
  • eliminar app.service.ts ,
  • Abra app.module.ts ,
  • Eliminar la referencia a AppController en los controllers array y las importaciones,
  • Eliminar la referencia a AppService en los providers array y las importaciones.

Es posible que también deba cambiar el README.md para cumplir con sus especificaciones.

Tu app.module.ts el archivo debería verse así,

//app.module.ts

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

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

Variables Ambientales

Como buena práctica, parte de la información confidencial de su código no debe hacerse pública. Por ejemplo, su PORT y su MongoDB URI .

Arreglemos esto en tu código.

En tu ejecución de terminal

npm i dotenv

Luego crea un .env archivo en su directorio y agréguelo a su .gitignore expediente. Guarda tu PORT variable, también tendrá que almacenar su MongoDB URI más tarde en el mismo lugar. Ahora reemplace el PORT expuesto en tu main.ts expediente. Para hacer esto, importe el dotenv paquete y llame al .config() método en él.

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

Este debería ser tu main.ts archivo después de seguir los pasos anteriores.

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

Generación de Módulos

Para generar un módulo NestJS usando la CLI de NestJS, ejecute el fragmento de código a continuación.

nest generate module blogs

Este comando crea un blogs carpeta que contiene un blogs.module.ts archivo y registra BlogsModule en tu app.module.ts archivo.

Generación de interfaces

Generemos una interfaz usando la CLI de NestJS para realizar la verificación de tipos del objeto que representará las publicaciones de su blog. Para lograr esto primero tienes que cd en los blogs carpeta porque se recomienda que se almacenen cerca de los objetos de dominio a los que están asociados.

cd src/blogs

Luego ejecute el fragmento de código a continuación para generar la interfaz.

nest generate interface blogs

esto crea un blogs.interface.ts expediente. Aquí es donde definiremos nuestra interfaz. llamaremos a la interfaz BlogsInterface .

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

antes de ejecutar más comandos en su terminal, recuerde cd fuera del src carpeta y de vuelta a su carpeta raíz ejecutando

cd ../..

Servicios de generación y controladores

Deberá generar una clase de servicio para almacenar y recuperar datos y manejar toda la lógica y una clase de controlador para manejar todas las solicitudes entrantes y las respuestas salientes.

Servicio

Para generar un servicio, ejecute el siguiente comando,

nest generate service blogs

Este comando crea dos archivos blogs.service.spec.ts y el blogs.service.ts y da de alta el servicio en los providers matriz en blogs.module.ts .

Controlador

Para generar un controlador, ejecute el siguiente comando,

nest generate controller blogs

Este comando crea dos archivos blogs.controller.spec.ts y el blogs.controller.ts y registra el controlador en los controllers matriz en blogs.module.ts .

Con esto, la estructura de su blog está casi completa, solo necesita hacer el BlogsService accesible a otras partes de su programa. Puede lograr esto creando un exports matriz en blogs.module.ts y registrando el BlogsService en esa 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(Mangosta).

Instale mongoose ejecutando,

npm install --save @nestjs/mongoose mongoose

Después de la instalación, importa {MongooseModule} de '@nestjs/mongoose’ en tu app.module.ts expediente. Luego tome su MongoDB URI y guárdelo en su .env expediente. Repita los pasos para importar dotenv en el app.module.ts expediente. Luego en las imports matriz llama al .forRoot() método que toma su MongoDB URI como argumento en MongooseModule . Similar a mongoose.connect() en aplicaciones express regulares.

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

Creación de un esquema.

Vamos a crear un esquema para definir la forma de los blogs de nuestra colección. Para ello,

  • Cree una carpeta dentro de sus blogs carpeta, asígnele el nombre schemas ,
  • Dentro de los schemas carpeta, cree un archivo y llámelo blogs.schema.ts .

Entonces,

En primer lugar, tendrás que,

  • Importar el prop decorador, el Schema decorador y SchemaFactory de @nestjs/mongoose ,
  • Crear una clase Blog y exportarlo,
  • Convierta la clase en un esquema colocando @Schema() decorador por encima de la clase,
  • Cree una constante BlogSchema , asigne el valor de retorno de llamar a .createForClass(Blog) con el nombre de tu clase como argumento en SchemaFactory que importó anteriormente.
//blogs.schema.ts

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

@Schema()
export class Blog {}

export const BlogSchema = SchemaFactory.createForClass(Blog);

Luego deberá definir las propiedades del Esquema.

Para definir una propiedad en el esquema, deberá marcar cada una de ellas con @prop() decorador. El @prop decorador acepta un objeto de opciones o una declaración de tipo complejo. Las declaraciones de tipos complejos podrían ser matrices y declaraciones de tipos de objetos anidados.

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

Siguiente importación { Document } de 'mongoose' .

Luego cree un tipo de unión con la clase Schema y el Document importado . Al igual que,

//blogs.schema.ts

import { Document } from 'mongoose';

export type BlogDocument = Blog & Document;

Su blogs.schema.ts final el archivo debería verse así,

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

Deberá importar todo a su blogs.module.ts expediente. Para lograr esto necesitarás,

  • Importar {MongooseModule} de '@nestjs/mongoose’ ,
  • Importar {Blog, BlogSchema} de './schemas/blogs.schema'
  • Crear una imports matriz dentro del @module decorador
  • Llama al .forFeature() método en MongooseModule . Esto toma una matriz que contiene un objeto que define un name y un schema propiedad que debe establecerse en su Blog.name y tu BlogSchema respectivamente.
@Module({
  imports: [
    MongooseModule.forFeature([{ name: Blog.name, schema: BlogSchema }]),
  ],

Esquema de inyección

Deberá inyectar el Blog modelo en blogs.service.ts usando el @InjectModel() decorador. Para conseguirlo tendrás que

  • importar { Model } de 'mongoose' ,
  • importar { InjectModel } de '@nestjs/mongoose' ,
  • Importar {Blog, BlogDocument} de './schemas/blogs.schema’ ,
  • Crear un constructor dentro del BlogsService clase,
  • Declarar un private variable y llámelo blogModel y asigne un tipo de Model<BlogDocument> lo. Todos los métodos de mangosta serán llamados en esta variable.

Recuerda eso, BlogDocument es el tipo de unión del Blog clase y el Model de Mongoose que creaste anteriormente. Se utiliza como tipo genérico para su variable.

  • Decora blogModel con @InjectModel() y pase Blog.name como argumento.
constructor(
    @InjectModel(Blog.name)
    private blogModel: Model<BlogDocument>,
  ) {}

Cómo funciona el enrutamiento

A estas alturas ya debe haber notado que @Controller decorador tiene la cadena 'blogs' pasó en él. Esto significa que el controlador enviará todas las respuestas y manejará todas las solicitudes realizadas en http://localhost/3000/blogs .

A continuación, implementará el servicio y la lógica del controlador.

Lógica de servicio y controlador.

Finalmente es hora de implementar su funcionalidad CRUD.

Antes de comenzar, deberá configurar su controlador. Comienza importando algo de HTTP decoradores de métodos en su controlador.

//blogs.controller.ts

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

Luego, deberá importar el servicio y registrarlo para poder acceder a él e importar la interfaz para la verificación de tipos.

//blogs.controller.ts

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

Para registrar su servicio, cree un constructor dentro del BlogsController class y declarar un private readonly variable service y establezca su tipo en BlogsService .

constructor(private readonly service: BlogsService) {}

Ahora que ya está todo listo, comencemos.

Crear

Lógica de servicio

Importar { BlogsInterface } de './blogs.interface' y agregue un async función al BlogsService clase llamada createBlog , que tomará un parámetro blog , con su tipo como BlogInterface , y su tipo de retorno como Promise con un <Blog> genérico tipo.

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

Lógica del controlador

En tu BlogsController clase agrega un async función a la clase. Llámalo createBlog y márcalo con @Post decorador que lo define como POST solicitud.createBlog toma un parámetro blog , con su tipo como BlogInterface . Marque el parámetro con @Body decorador que extrae todo el body objeto del req objeto y rellena el parámetro decorado con el valor de body .

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

Leer

Agregue dos async métodos, uno para devolver una sola publicación de blog y el segundo para devolver todas las publicaciones de blog.

Lógica de servicio

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

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

Lógica del controlador

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

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

El async las funciones están marcadas con @Get decorador que lo define como GET solicitud.

El segundo async el decorador de la función tiene un argumento ':id' . Que es lo que pasarás al @Param decorador. El parámetro está marcado con @Param('id') que extrae los params propiedad de req objeto y rellena el parámetro decorado con el valor de params .

Actualizar

Implementemos la lógica para PUT solicitud.

Lógica de servicio

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

Lógica del controlador

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

El async el segundo parámetro de la función está marcado con @Body() decorador que extrae todo el body objeto del req objeto y rellena el parámetro decorado con el valor de body .

Eliminar

Implementemos la lógica para delete solicitudes.

Lógica de servicio

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

La Promise el tipo genérico es void porque un Delete solicitud devuelve una promesa vacía.

Lógica del controlador

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

Prueba de la API

Para probar esta API, debe usar una herramienta de prueba de API. Para este artículo, usaré una popular herramienta de prueba de API llamada Postman. Usaré datos aleatorios sobre temas populares para probar.

Crear

Haz un POST solicitud a http://localhost/3000/blogs con los siguientes objetos JSON, esto agregará todos los datos a su base de datos.

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

Debería obtener un 201 respuesta y el blog creado con una fecha y un _id añadido.

Leer

Haz un GET solicitud a http://localhost/3000/blogs . Esto debería devolver un

200 respuesta con una matriz de todos los datos que agregó anteriormente. Copie el _id propiedad de uno de los objetos de la matriz.

Haz otro GET solicitud a http://localhost/3000/blogs/id con el id previamente copiado. Esto debería devolver un 200 respuesta con los datos del objeto cuyo id se utilizó para realizar la solicitud.

Actualizar

Haz un PUT solicitud a http://localhost/3000/blogs/id con los datos de abajo. El id debe ser reemplazado por el que copió anteriormente. Esto debería devolver un 200 respuesta y actualiza el objeto que lleva el id entre bastidores. si ejecuta otro GET solicitud debe obtener el objeto actualizado.

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

Eliminar

Haz un DELETE solicitud a http://localhost/3000/blogs/id .Esto debería devolver un 200 respuesta y elimina el objeto que lleva el id entre bastidores. si ejecuta otro GET solicitud, no verá el objeto eliminado.

Conclusión

Así que finalmente llegamos al final de este artículo. Recapitulemos lo que ha cubierto.

  • Qué es NestJS,
  • Terminologías en NestJS,
  • Crear una aplicación NestJS,
  • Integrando MongoDB en una aplicación NestJS,
  • Manipulación y aplicación NestJS,

Eso es mucho, felicitaciones por llegar tan lejos.

Puedes encontrar el código en github.

¡Buena suerte en tu viaje con NestJS!