Miguel Mendoza
Un curso de pruebas unitarias para Nest estaría increíble! 🤩. Sería un PLUS para aprender todas las bases 😎.
JOSE ALCIVAR GARCIA
seria genial!!! porque de aqui este curso esta completooo!!
Jose Daniel Barría Reyes
De acuerdo!
¿Cómo gestionar la agregación de órdenes en NestJS?
En el desarrollo de aplicaciones con NestJS, la gestión de órdenes de compra puede simplificarse creando un servicio y un controlador dedicados. Este proceso se inicia utilizando el Nest CLI para generar los componentes necesarios. Vamos a explorar cómo se hace.
¿Cómo generamos el servicio y el controlador?
Para iniciar, en la terminal empleamos el comando nest generate, especificando que queremos un servicio (s) y un controlador. La estructura se ubicará bajo el módulo de usuarios, dado su contexto en la aplicación:
nest generate service users/service/ordered --flat nest generate controller users/controllers/ordered --flat
Con estos pasos se crean los archivos básicos y se agregan al UserModule sin necesidad de ningún ajusto adicional.
¿Qué es un DTO y cómo se crea?
El DTO (Data Transfer Object) delimita los datos que serán transferidos al momento de crear una orden. Creamos una clase CreateOrderDTO:
export class CreateOrderDTO { @IsPositive() @IsNotEmpty() @ApiProperty() readonly customerId: number; }
Este DTO identifica al cliente relacionado, validando que la información sea positiva y no vacía, integrándose con la documentación Swagger.
¿Cómo desarrollamos las funciones CRUD?
En el OrderedService, implementamos las funciones básicas CRUD: "crear", "actualizar" y "eliminar", apoyándonos en dos repositorios: OrderRepo y CustomerRepo. Utilizamos InjectRepository:
@Injectable() export class OrderedService { constructor( @InjectRepository(Order) private orderRepo: Repository<Order>, @InjectRepository(Customer) private customerRepo: Repository<Customer> ) {} // Funciones CRUD aquí... }
Para crear una orden, solo es necesario el CustomerID, y la instancia de la orden se guarda en el repositorio correspondiente.
Incorporación de ítems en la orden
Una vez establecida la orden de compra, el siguiente paso es gestionar la inclusión de productos, tratados como ítems en una entidad ternaria. Esta sección detalla cómo se procede.
¿Cómo creamos el DTO para los ítems?
Para gestionar los ítems, creamos un DTO OrderItemDTO que detalla los elementos necesarios:
export class CreateOrderItemDTO { @IsPositive() @IsNotEmpty() @ApiProperty() readonly orderId: number; @IsPositive() @IsNotEmpty() @ApiProperty() readonly productId: number; @IsPositive() @IsNotEmpty() @ApiProperty() readonly quantity: number; }
Este DTO especifica la orden y el producto a los que se añaden los ítems, incluyendo la cantidad.
¿Cómo se estructura el servicio y controlador para los ítems de la orden?
Al crear ítems, se replican el servicio y controlador, asegurando que el servicio maneje la lógica de negocio:
@Injectable() export class OrderItemService { constructor( @InjectRepository(OrderItem) private itemsRepo: Repository<OrderItem>, @InjectRepository(Order) private orderRepo: Repository<Order>, @InjectRepository(Product) private productRepo: Repository<Product> ) {} async createItem(data: CreateOrderItemDTO): Promise<OrderItem> { const { orderId, productId, quantity } = data; const order = await this.orderRepo.findOne(orderId); const product = await this.productRepo.findOne(productId); const item = this.itemsRepo.create({ order, product, quantity }); return this.itemsRepo.save(item); } }
Además, en el OrdersController se implementa un POST para agregar ítems:
@Post('/order-items') async addItem(@Body() dto: CreateOrderItemDTO): Promise<OrderItem> { return this.orderItemService.createItem(dto); }
Comprobación y optimización
Finalmente, revisamos la gestión mediante herramientas como Insomnia y pgAdmin para verificar la correcta inserción de datos y resolver inconvenientes de inyección de dependencias mediante la exportación correcta de módulos. Validamos las relaciones complejas en entidades y garantizamos que los endpoints expongan toda la información necesaria.
Este enfoque facilita el manejar relaciones muchos a muchos en bases de datos utilizando una estructura de módulos bien definida, inyecciones de dependencias y validaciones de DTO que protegen y estructuran la integridad de nuestros datos, ofreciendo una manera eficiente de administrar las operaciones de la aplicación.
Miguel Mendoza
Un curso de pruebas unitarias para Nest estaría increíble! 🤩. Sería un PLUS para aprender todas las bases 😎.
JOSE ALCIVAR GARCIA
seria genial!!! porque de aqui este curso esta completooo!!
Jose Daniel Barría Reyes
De acuerdo!
Nahuel Soma
Para quien venga llevando el curso con TypeORM 0.3, las nested relations se resueven así:
// orders.service.ts relations: { items: { product: true, }, },
Y servicio quedaría de la siguiente forma:
// orders.service.ts async findOne(id: number) { const order = await this.orderRepo.findOne({ where: { id, }, relations: { items: { product: true, }, }, }); if (!order) { throw new NotFoundException('not found'); } return order; }
Fuente: https://github.com/typeorm/typeorm/releases/tag/0.3.0
Ángel David Roque Ayala
Renato Renzo Luna Herrera
nest g s users/services/order-item --flat
Ángel David Roque Ayala
andrstejada12
Un curso de prueba unitarias en Nest js Por favor
Ricardo Tribaldos Hernández
Cuando usan el comando generate del CLI pueden pasarle el argumento --no-spec para que no cree los archivos para pruebas unitarias
Gerardo Guevara
Esta super bien la clase, pero como sujerencia estas deben ser mas cortas. Lo ideal es que duren un maximo de 10 minutos porque de lo contrario es mayor el esfuerzo mental que un alumno debe hacer y facilmente pierda el hilo del tema.
Carlos Diaz
Incluyan el curso de Prueba en Nest
Jhon Stiven Granada Acevedo
Si tienen problemas con el findOne lo hice asi
this.orderRepo.findOne({ where: { id: data.orderId } });
Omar Blanco
Inlcuyan el curso de pruebas unitarias para nest
Jorge Blanco
Por favor el Cursos de Pruebas unitarias , seria Genial Gracias NicoBite
Brandon Lee Aguero Fernandez
Ya pasaron años, pero por favor un curso de testing con NestJS
Ricardo Tribaldos Hernández
Carlos Alberto Cabrera Quiroga
en este gurso si seria con API REST y no graphQL
Carlos Alberto Cabrera Quiroga
No es mejor practica traer el servicio de customers y productos al servicio de ordenes, en lugar de injectar el repositorio( de productos y customers) en el servicio de ordenes?
Gustavo Restrepo
Eso depende, si se usa una sola función del servicio de customers o se va a hacer algo diferente a lo que hay en el servicio de customers entonces no hay necesidas, pero si vas a usar varios de los métodos de ese servicio entonces sí es mejor traer el servicio que usar el repositorio.
Gonzalo Chaparro
Profe: Al momento de crear una nuevo item de una orden, ya estoy validando que efectivamente tanto la orden como el producto existan en la base de datos, lo que no he podido validar es que si ya existe un item con esa misma orden y ese mismo producto asociado no lo ingrese a la base de datos: Acà mi còdigo:
async create(data: CreateOrderItemDto){
const order = await this.orderRepo.findOne(data.orderId);
const product = await this.productRepo.findOne(data.productId);
console.log(order);
console.log(product);
if(product !== undefined && order !== undefined){
const item = new OrderItem();
item.order = order;
item.product = product;
item.quantity = data.quantity;
return this.itemRepo.save(item);
}else{
if(product === undefined && order === undefined){
console.log("Orden y producto no existen");
}else if(order === undefined){
console.log("Orden no existe");
}else if(product === undefined){
console.log("Producto no existe");
}
}
}
Alguien se le ocurre como solucionarlo?
Heyner Javier Marmol Verbel
Puedes utilizar el decorator @Unique en el entity, a continuación te dejo un ejemplo:
@Entity() @Unique(["productId", "order"]) export class OrderItem { ... }
Te dejo un enlace a la documentación: Decorator Reference | TypeORM @Unique
Ivan Camilo Buitrago Buitrago
a este punto del curso tengo una duda, por que esta combinando dentro de users lo que seria customers y ordenes no se supondria que como buena practica serian cada uno un modulo por separado y no estar todo dentro del modulo users?
Mis apuntes
nest g s users/services/orders --flat nest g co users/controllers/orders --flat nest g co users/services/order-item --flat nest g co users/controllers/order-item --flat
// src\users\dtos\order.dto.ts import { ApiProperty, PartialType } from "@nestjs/swagger"; import { IsNotEmpty, IsPositive } from "class-validator"; export class CreateOrderDto { @IsPositive() @IsNotEmpty() @ApiProperty() readonly customerId: number; } export class UpdateOrderDto extends PartialType(CreateOrderDto){}
// src\users\services\orders.service.ts import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { CreateOrderDto, UpdateOrderDto } from '../dtos/order.dto'; import { Customer } from '../entities/customer.entity'; import { Order } from '../entities/order.entity'; @Injectable() export class OrdersService { constructor( @InjectRepository(Order) private orderRepo:Repository<Order>, @InjectRepository(Customer) private customerRepo:Repository<Customer> ){} findAll(){ return this.orderRepo.find(); } async findOne(id: number){ const order = await this.orderRepo.findOne(id); if(!order) { throw new NotFoundException(`order ${id} not found`); } return order; } async create(data: CreateOrderDto){ const order = new Order(); if (data.customerId) { const customer = await this.customerRepo.findOne(data.customerId); order.customer = customer; } return this.orderRepo.save(order); } async update(id:number, changes:UpdateOrderDto){ const order = await this.orderRepo.findOne(id); if (changes.customerId) { const customer = await this.customerRepo.findOne(changes.customerId); order.customer = customer; } return this.orderRepo.save(order); } delete(id:number){ return this.orderRepo.delete(id); } }
// src\users\controllers\orders.controller.ts import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put } from '@nestjs/common'; import { CreateOrderDto, UpdateOrderDto } from '../dtos/order.dto'; import { OrdersService } from '../services/orders.service'; @Controller('orders') export class OrdersController { constructor(private ordersService: OrdersService){} @Get('') findAll(){ return this.ordersService.findAll(); } @Get(':id') findOne( @Param('id', ParseIntPipe) id: number ){ return this.ordersService.findOne(id); } @Post('') create(@Body() payload: CreateOrderDto){ return this.ordersService.create(payload); } @Put(':id') update( @Param('id', ParseIntPipe) id:number, @Body() payload: UpdateOrderDto ){ return this.ordersService.update(id, payload); } @Delete(':id') delete(@Param('id') id: number){ return this.ordersService.delete(id); } }
// src\users\dtos\order-item.dto.ts import { ApiProperty, PartialType } from "@nestjs/swagger"; import { IsNotEmpty, IsNumber, IsPositive } from "class-validator"; export class CreateOrderItemDto { @IsNumber() @IsPositive() @IsNotEmpty() @ApiProperty() readonly orderId: number; @IsNumber() @IsPositive() @IsNotEmpty() @ApiProperty() readonly productId: number; @IsNumber() @IsPositive() @IsNotEmpty() @ApiProperty() readonly quantity: number; } export class UpdateOrderItemDto extends PartialType(CreateOrderItemDto){}
// src\products\products.module.ts @Module({ ... exports: [..., TypeOrmModule], // export TypeOrmModule }) export class ProductsModule {}
// src\users\services\order-item.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Product } from 'src/products/entities/product.entity'; import { Repository } from 'typeorm'; import { CreateOrderItemDto, UpdateOrderItemDto } from '../dtos/order-item.dto'; import { OrderItem } from '../entities/order-item.entity'; import { Order } from '../entities/order.entity'; @Injectable() export class OrderItemService { constructor( @InjectRepository(Product) private productRepo:Repository<Product>, @InjectRepository(Order) private orderRepo:Repository<Order>, @InjectRepository(OrderItem) private orderItemRepo: Repository<OrderItem> ){} async create(data: CreateOrderItemDto){ const order = await this.orderRepo.findOne(data.orderId); const product = await this.productRepo.findOne(data.productId); const newOrderItem = new OrderItem(); newOrderItem.order = order; newOrderItem.product = product; newOrderItem.quantity = data.quantity; return this.orderItemRepo.save(newOrderItem) } }
// src\users\controllers\order-item.controller.ts import { Body, Controller, Post } from '@nestjs/common'; import { CreateOrderItemDto } from '../dtos/order-item.dto'; import { OrderItemService } from '../services/order-item.service'; @Controller('order-item') export class OrderItemController { constructor(private orderItemService: OrderItemService){} @Post('') create(@Body() payload: CreateOrderItemDto){ return this.orderItemService.create(payload); } }
// src\users\services\orders.service.ts async findOne(id: number){ const order = await this.orderRepo.findOne(id, { relations: ["items", "items.product"] // Agregamos relacion }); if(!order) { throw new NotFoundException(`order ${id} not found`); } return order; }
Mi Solucion al Reto
findAll, findOne, update, delete
// src\users\services\order-item.service.ts findAll(){ return this.orderItemRepo.find({ relations: ['order', 'order.customer'] }); } findOne(id: number){ return this.orderItemRepo.findOne(id,{ relations: ['order', 'product'] }); } async update(id: number, changes: UpdateOrderItemDto){ const orderItem = await this.orderItemRepo.findOne(id); if (changes.orderId) { const order = await this.orderRepo.findOne(changes.orderId); orderItem.order = order; } if (changes.productId) { const product = await this.productRepo.findOne(changes.productId); orderItem.product = product; } if(changes.quantity){ orderItem.quantity = changes.quantity; } return this.orderItemRepo.save(orderItem); } delete(id: number){ return this.orderItemRepo.delete(id); }
// src\users\controllers\order-item.controller.ts @Get('') findAll(){ return this.orderItemService.findAll(); } @Get(':id') findOne(@Param('id', ParseIntPipe) id: number){ return this.orderItemService.findOne(id); } @Put(':id') update( @Param('id', ParseIntPipe) id: number, @Body() payload: UpdateOrderItemDto ){ return this.orderItemService.update(id, payload); } @Delete(':id') delete(@Param('id') id: number){ return this.orderItemService.delete(id); }
Otro tip, pueden usar nest generate resource <nombre-del-recurso> para autogenerar un CRUD (link):
$ nest g resource users > ? What transport layer do you use? GraphQL (code first) > ? Would you like to generate CRUD entry points? Yes > CREATE src/users/users.module.ts (224 bytes) > CREATE src/users/users.resolver.spec.ts (525 bytes) > CREATE src/users/users.resolver.ts (1109 bytes) > CREATE src/users/users.service.spec.ts (453 bytes) > CREATE src/users/users.service.ts (625 bytes) > CREATE src/users/dto/create-user.input.ts (195 bytes) > CREATE src/users/dto/update-user.input.ts (281 bytes) > CREATE src/users/entities/user.entity.ts (187 bytes) > UPDATE src/app.module.ts (312 bytes)