¿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 loscontrollers
array y las importaciones, - Eliminar la referencia a
AppService
en losproviders
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 nombreschemas
, - Dentro de los
schemas
carpeta, cree un archivo y llámeloblogs.schema.ts
.
Entonces,
En primer lugar, tendrás que,
- Importar el
prop
decorador, elSchema
decorador ySchemaFactory
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 enSchemaFactory
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 enMongooseModule
. Esto toma una matriz que contiene un objeto que define unname
y unschema
propiedad que debe establecerse en suBlog.name
y tuBlogSchema
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 delBlogsService
clase, - Declarar un
private
variable y llámeloblogModel
y asigne un tipo deModel<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 paseBlog.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!