Dejo un link interesante del querido Martin Fowler acerca de este patron https://martinfowler.com/bliki/LocalDTO.html
Introducción a NestJS
¿Qué es NestJS?
Crea tu primer proyecto con NestJS
Estructura de aplicaciones en NestJS
Presentación del proyecto: Platzi Store
Repaso a TypeScript: tipos y POO
REST API
Introducción a controladores
GET: cómo recibir parámetros
GET: parámetros query
Separación de responsabilidades
Instalación de Postman o Insomnia
Qué es el método POST
Métodos PUT y DELETE para editar y eliminar
Códigos de estado o HTTP response status codes
Integridad de datos
Introducción a servicios: crea tu primer servicio
Implementando servicios en tu controlador
Manejo de errores con throw y NotFoundException
Introducción a pipes: usa tu primer pipe
Crea tu propio pipe
Creando Data Transfers Objects
Validando parámetros con class-validator y mapped-types
Cómo evitar parámetros incorrectos
Próximos pasos
Reto: controladores y servicios restantes
Continúa con el Curso de NestJS: Programación Modular
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
NestJS utiliza el concepto de Objetos de Transferencia de Datos, o simplemente abreviado como DTO, para el tipado de datos y su segurización.
Los DTO no son más que clases customizadas que tu mismo puedes crear para indicar la estructura que tendrán los objetos de entrada en una solicitud.
Crea un nuevo archivo que por lo general lleva como extensión .dto.ts
para indicar que se trata de un DTO.
// products.dto.ts
export class CreateProductDTO {
readonly name: string;
readonly description: string;
readonly price: number;
readonly image: string;
}
La palabra reservada readonly
es propia de TypeScript y nos asegura que dichos datos no sean modificados.
Crea tantos atributos como tu clase CreateProductDTO necesite para dar de alta un nuevo producto.
Importa la clase en tu controlador para tipar el Body del endpoint POST para la creación de un producto.
import { CreateProductDTO } from 'products.dto.ts';
@Post('product')
createProducto(@Body() body: CreateProductDTO): any {
// ...
}
De esta forma, ya conoces la estructura de datos que tendrá el parámetro body
previo a la creación de un producto.
// src/dtos/products.dtos.ts
export class CreateProductDto {
readonly name: string;
readonly description: string;
readonly price: number;
readonly stock: number;
readonly image: string;
}
export class UpdateProductDto {
readonly name?: string;
readonly description?: string;
readonly price?: number;
readonly stock?: number;
readonly image?: string;
}
// src/controllers/products.controller.ts
export class ProductsController {
@Post()
create(@Body() payload: CreateProductDto) { // 👈 Dto
...
}
@Put(':id')
update(
@Param('id') id: string,
@Body() payload: UpdateProductDto // 👈 Dto
) {
...
}
}
// src/services/products.service.ts
export class ProductsService {
create(payload: CreateProductDto) { // 👈 Dto
...
}
update(id: number, payload: UpdateProductDto) { // 👈 Dto
...
}
}
Contribución creada por: Kevin Fiorentino.
Aportes 26
Preguntas 18
Dejo un link interesante del querido Martin Fowler acerca de este patron https://martinfowler.com/bliki/LocalDTO.html
readonly???
el signo ? para variables opcionales???
Se ve que Typescript si que promete
La documentacion de Nest recomienda crear los DTOs con classes y no con interfaces.
import { PartialType } from '@nestjs/mapped-types';
import { IsString, IsNumber, IsOptional, IsNotEmpty } from 'class-validator';
export class CreateProductDto {
@IsNotEmpty()
@IsString()
readonly name: string;
@IsString()
@IsOptional()
description?: string;
@IsNotEmpty()
@IsNumber({ allowNaN: false })
price: number;
@IsNotEmpty()
@IsNumber()
readonly stock: number;
@IsString()
readonly image: string;
}
export class UpdateProductDto extends PartialType(CreateProductDto) {
@IsOptional()
@IsString()
readonly name?: string;
@IsString()
@IsOptional()
description?: string;
@IsOptional()
@IsNumber({ allowNaN: false })
price?: number;
@IsOptional()
@IsNumber()
readonly stock?: number;
@IsOptional()
@IsString()
readonly image?: string;
}
Funciones de los DTOs:
Para no crear el IUpdateDto solo podrias utilizar algo como esto Partial<CreateProductDto>
Nico en tu método update de tu ProductsController haces una operación PUT pero en el ejemplo la operación no es de remplazo sino de actualización parcial, es decir un PATCH
dejo un articulo al respecto: https://rapidapi.com/blog/put-vs-patch/
Los data transfers object o DTO por sus siglas, es una guía para que el desarrollador y aquellos que consumen la API sepan que de forma espera tener el cuerpo de la solicitud.
No ejecuta ninguna validación de clases es mas una guía
Creamos una carpeta dtos
en el cual creamos nuestro archivo products.dto.ts
aqui vamos a especificar que campos van a obtener el cuerpo de la solicitud:
export class CreateProductDto {
readonly name: string;
readonly description: string;
readonly price: number;
readonly stock: number;
readonly imag: string;
}
Readonly:
Indica que el campo no podrá ser editado.
Y luego lo importamos en nuestro controlador o services:
import { CreateProductDto } from './../../dtos/products.dto'
@Post()
create(@Body() payload: CreateProductDto) {
return this.productsService.create(payload);
}
Los Data Transfers Objects (DTO) nos permite tipar y validar los datos que provienen de la petición (body)
Entities vs DTOs
Estas son las grandes ventajas de TypeScript, en equipos grandes ayuda mucho porque ya sabes qué tipo de dato esperar y el IntelliSense te dice exactamente lo que necesitas saber 😎
Con tipos también funciona, y con partial convierto todos a valores opcionales:
export type ICreateProductDto = {
readonly name: string;
readonly description: string;
readonly price: number;
readonly stock: number;
readonly image: string;
readonly brand: string;
}
export type IUpdateProductDto = Partial<ICreateProductDto>;
Super interesante los DTOs, no conocia este concepto. Cuando trabajo con Angular, utilizo interfaces para tipar los payloads. Por ejemplo, en el caso del payload de productos yo haria lo siguiente:
export interface ProductRequest {
name: string;
description: string;
stock: number:
image: string;
}
funciona exactamente igual. Lo diferente, que me llamo la atencion es que con los DTOs puedes especificar que el atributo sea solo de lectura con readonly. Entonces esto da un mayor beneficio porque proteges mas los datos…muy interesante.
Definitivamente la única forma de aprender todo estos temas de configuración es poniéndolo en práctica en proyectos, pequeños, medianos o grandes.
Te falto definir el “Id” del producto en tu DTO, y el nombre de cada archivo DTO debe ser creado de manera “Singular”.
Es una obra maestra esto de los DTO
No estas usando métodos de la clase, así que puedes seguir usando interfaces con readonly, adicional yo suelo tener clases que tienen internamente un método que convierte a DTO (esto en el front), en Nest se podría implementar una interfaz DTO, que la clase lo reciba en el constructor y lo guarde en el formato que necesita
Crear el dto del UpdateProducts
Validar el @body y el servicio con el Data Transfer Objects (dtos) nos aseguramos que nos envien los datos en los tipos correctos
Implementar el Data Transfers Objects (dto) en el Controlador
readonly - solo lectura no es para manipular
Crear datos de transferencia de productos
Data Transfers Objects son objetos que nos permiten validar (tipar informacion) cuando se envían datos de transferencia
Pero al final TypeScript solo es experiencia de desarrollo
Los DTO no serian igual que los types
en el frront?
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?
o inicia sesión.