Conectarse a una base de datos implica la posibilidad de manipular en su totalidad los datos desde su creación, modificación y eliminación, además de la lectura de los mismos.
Crear documento
La creación de un registro es bastante sencilla, basta con pasarle los datos necesarios para la creación de un nuevo documento.
Recuerda que puedes apoyarte en los DTO de NestJS para el tipado de datos. El productModel espera que le envíes un objeto que contenga las mismas propiedades que el esquema que hayas creado para la colección.
Actualizar documento
La actualización de un registro conlleva dos partes, comprobar que el mismo exista para su posterior actualización. Afortunadamente, Mongoose puede realizar las dos acciones en una.
Para la actualización de un documento, la función findByIdAndUpdate primero busca el registro por ID y si lo encuentra, lo actualiza. Aquí ya es necesario un poco de conocimiento en el lenguaje de consulta de MongoDB para utilizar el $set que actualizará los campos que se le indique.
Eliminar documento
La eliminación de un registro es similar a la actualización. El documento debe existir para su posterior eliminación. La función findByIdAndDelete lo hará por nosotros.
Revisa que uses el ApiProperty en todos los attrs del Dto para que se haga la actualización correctamente.
// src/products/dtos/products.dtos.tsimport{ApiProperty,PartialType}from'@nestjs/swagger';exportclassCreateProductDto{@IsString()@IsNotEmpty()@ApiProperty({ description:`product's name`})// 👈 use ApiPropertyreadonly name:string;@IsString()@IsNotEmpty()@ApiProperty()// 👈 use ApiPropertyreadonly description:string;@IsNumber()@IsNotEmpty()@IsPositive()@ApiProperty()// 👈 use ApiPropertyreadonly price:number;@IsNumber()@IsNotEmpty()@ApiProperty()// 👈 use ApiPropertyreadonly stock:number;@IsUrl()@IsNotEmpty()@ApiProperty()// 👈 use ApiPropertyreadonly image:string;}exportclassUpdateProductDtoextendsPartialType(CreateProductDto){}
Creación, actualización y eliminación de productos en MongoDB
Creación, actualización y eliminación de productos en MongoDB
Recursos
Resumen
Conectarse a una base de datos implica la posibilidad de manipular en su totalidad los datos desde su creación, modificación y eliminación, además de la lectura de los mismos.
Crear documento
La creación de un registro es bastante sencilla, basta con pasarle los datos necesarios para la creación de un nuevo documento.
// modules/products/products.service.ts
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateProductDto } from './product.dto';
@Injectable()
export class ProductsService {
constructor(@InjectModel(Product.name) private productModel: Model<Product>) {}
create(data: CreateProductDto) {
const newProduct = new this.productModel(data);
return newProduct.save();
}
}
Recuerda que puedes apoyarte en los DTO de NestJS para el tipado de datos. El productModel espera que le envíes un objeto que contenga las mismas propiedades que el esquema que hayas creado para la colección.
Actualizar documento
La actualización de un registro conlleva dos partes, comprobar que el mismo exista para su posterior actualización. Afortunadamente, Mongoose puede realizar las dos acciones en una.
// modules/products/products.service.ts
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { UpdateProductDto } from './product.dto';
@Injectable()
export class ProductsService {
constructor(@InjectModel(Product.name) private productModel: Model<Product>) {}
update(id: string, changes: UpdateProductDto) {
return this.productModel
.findByIdAndUpdate(id, { $set: changes }, { new: true })
.exec();
}
}
Para la actualización de un documento, la función findByIdAndUpdate primero busca el registro por ID y si lo encuentra, lo actualiza. Aquí ya es necesario un poco de conocimiento en el lenguaje de consulta de MongoDB para utilizar el $set que actualizará los campos que se le indique.
Eliminar documento
La eliminación de un registro es similar a la actualización. El documento debe existir para su posterior eliminación. La función findByIdAndDelete lo hará por nosotros.
// modules/products/products.service.ts
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
@Injectable()
export class ProductsService {
constructor(@InjectModel(Product.name) private productModel: Model<Product>) {}
remove(id: string) {
return this.productModel.findByIdAndDelete(id);
}
}
Endpoints para crear, actualizar y eliminar
Ya solo queda construir los endpoints necesarios para realizar cada operación en la base de datos.
En este momento, los throw new NotFoundException dejaron de funcionar. Para solucionarlo debemos resolver la promesa que regresa productModel usando async / await por ejemplo.
De otra forma, el modelo siempre retornara algo, retornara "Promise { <pending> }" sin importar que exista o no el producto en la base de datos.
async findOne(id: string) {
const product = await this.productModel.findById(id).exec();
if (!product) {
throw new NotFoundException(`Product #${id} not found`);
}
return product;
}
Como se podría hacer una paginación con la siguiente estructura como el siguiente ejemplo en una consulta que no demore tanto
{
data: [], //trae los 10 primeros registros debido al limit
total: 0, //Total registros de la consulta aplicando solo los filtros sin tener en cuenta el limit ni skip
limit: 10,
skip: 0,
}
Algo que no te mencionan es agregar todos los servicios de manera asíncrona, esto es debido a:
Mayor manejo y respuestas de los servidores.
Manejo de errores en caso de error.
Espera de las consultas a la base de datos y que no colapse.
Espero que ayude el aporte.
En este momento, los throw new NotFoundException dejaron de funcionar. Para solucionarlo debemos resolver la promesa que regresa productModel usando async / await por ejemplo.
De otra forma, el modelo siempre retornara algo, retornara "Promise { <pending> }" sin importar que exista o no el producto en la base de datos.
asyncfindOne(id: string){const product =awaitthis.productModel.findById(id).exec();if(!product){thrownewNotFoundException(`Product #${id} not found`);}return product;}
Aquí están todos los métodos usando async / await
asyncfindAll(){returnawaitthis.productModel.find().exec();}asyncfindOne(id: string){const product =awaitthis.productModel.findById(id).exec();if(!product){thrownewNotFoundException(`Product #${id} not found`);}return product;}asynccreate(data:CreateProductDto){const newProduct =newthis.productModel(data);returnawait newProduct.save();}asyncupdate(id: string,changes:UpdateProductDto){const product =awaitthis.productModel.findByIdAndUpdate(id,{$set: changes },{new:true}).exec();if(!product){thrownewNotFoundException(`Product #${id} not found`);}return product;}asyncremove(id: string){const product =awaitthis.productModel.findByIdAndRemove(id);if(!product){thrownewNotFoundException(`Product #${id} not found`);}return product;}
Muchas gracias, ya se me hacia raro que no se utilizara esto en NestJS al ser básicamente NodeJS
En mi caso no fue necesario indicar el ApiProperty a cada elemento de CreateProduct, quien más le paso lo mismo?
Que raro en las últimas versiones que he trabajado toca agregar el ApiProperty para que salga en la doc de Swagger
el metodo para borrar es
findByIdAndRemove
Cualquiera de los dos sirve, cual es la diferencia ?
Solo que para que funcione hay que añadirle el .exec() al final
Que diferencia hay entre findByIdAndRemove y findByIdAndDelete?
Son idénticas, dentro del paquete de mongoose se crean de la misma manera, aunque el findByIdAndRemove tiene otras opciones:
sort, select, rawResult, strict.
Por favor me pueden indicar donde esta el archivo de insommia, gracias
Como se podría hacer una paginación con la siguiente estructura como el siguiente ejemplo en una consulta que no demore tanto
{data:[],//trae los 10 primeros registros debido al limittotal:0,//Total registros de la consulta aplicando solo los filtros sin tener en cuenta el limit ni skiplimit:10,skip:0,}
No me funcionaba la solución para cuando no existe el id de un producto, por lo que diseñe esta solución espero me comenten que les parece saludos.
En el product.service.
findOne(id: string):Promise<any>{returnthis.productModel.findById(id).then((userDB)=>{if(!userDB)thrownewNotFoundException(`Product #${id} not found`);return{ok:true, userDB,};}).catch((err)=>{thrownewNotFoundException(`Product #${id} not found ${err}`);});}
cuando guardo solo me aparece esto, no me apaerece todo el modelo
{"_id":"6451be41d3994df39d3e6fdc","__v":0}
porque acá solo tocamos el código en products.service.ts y no en controllers? usando los decoradores como @Post() , @Put() o @Delete()
Porque esos métodos ya estaban creados así que solo trabajamos en el servicio para conectarlo a la DB de Mongo.
Delete Async:
asyncremove(id: string){const product =awaitthis.productModel.findOneAndDelete({_id: id });if(!product){thrownewNotFoundException(`Product #${id} not found`);}returntrue;}
Para los que quieran crear el metodo crear pero de multiple data, seria asi:
asynccreate(data:CreateProductDto){try{const newProduct =newthis.productModel(data);returnawait newProduct.save();}catch(error){thrownewBadRequestException(error.message);}}asyncupdate(id: string,data:UpdateProductDto){awaitthis.findOne(id);returnawaitthis.productModel.findByIdAndUpdate(id,{$set: data },{new:true})//con set solo modifico lo cambio.exec();}asyncremove(id: string){awaitthis.findOne(id);returnawaitthis.productModel.findByIdAndDelete(id);}