Introducción

1

Conexión de Bases de Datos con NetsGIS y Time OEM

2

Conexión a Bases de Datos con Next.js y TypeORM

Database

3

Instalación de Docker en Ubuntu paso a paso

4

Contenerización de Base de Datos: Ejecuta Postgres con Docker

5

Conexión a Postgres en Docker: Terminal y PgAdmin

6

Conexión a PostgreSQL desde NestJS usando Node Postgres

7

Conexión a PostgreSQL con Inyección de Dependencias en NestJS

8

Conexión a Bases de Datos con Variables de Entorno en Node.js

TypeORM

9

Implementación de TypeORM en Aplicaciones Node.js con NestJS

10

Creación de Entidades en ORMs con Decoradores en TypeScript

11

Uso de Repository Pattern con NestJS y TypeORM

12

Creación y Gestión de Productos en Base de Datos con Node.js y Postgres

13

Cambio de Postgres a MySQL con Docker y phpMyAdmin

Migraciones

14

Configuración de migraciones con TimeORM paso a paso

15

Configuración de TimeORM CLI para Migraciones en TypeScript

16

Ejecutar y gestionar migraciones con Time1M en Node.js

17

Manejo de Migraciones en Bases de Datos Relacionales

Relaciones

18

Relaciones 1 a 1 en Bases de Datos con TypeORM

19

Relaciones uno a muchos en bases de datos

20

Relaciones Uno a Muchos: Implementación en TypeORM

21

Gestión de Relaciones en Controladores: Productos y Marcas

22

Relaciones Muchos a Muchos en Bases de Datos con Time1M

23

Manipulación de Relaciones Muchos a Muchos en TypeORM

24

Manipulación de Arrays: Relación Muchos a Muchos en JavaScript

25

Relaciones Muchos a Muchos con Campos Agregados en TimeWareM

26

Tareas asíncronas con Promesas en JavaScript

Consultas

27

Paginación de Productos con Node.js y Query Params

28

Filtros y Operadores SQL en Time Wareham para Buscar Precios

29

Indexación en Bases de Datos: Mejora la Velocidad de tus Consultas

30

Buenas prácticas de nombramiento en bases de datos

31

Serialización y Transformación de Datos con Class Transformador

Migración a NestJS 9 y TypeORM 0.3

32

Actualización de dependencias en NEST y TypeORM

33

Migración de APIs: Actualización de Métodos Find en TimewareM 0.3

34

Migraciones con TimeWareM 0.3: configuración y ejecución segura

Próximos pasos

35

Solución a referencias circulares en NestJS

36

Conexión Nativa a Postgres con Node y Time Ware M

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Serialización y Transformación de Datos con Class Transformador

31/36
Recursos

¿Qué es la serialización y cómo se aplica en las APIs?

La serialización se refiere a la técnica de transformar datos antes de que un controlador los envíe al servicio o a una respuesta. Esta técnica es ampliamente utilizada en las APIs, ya sean REST o GraphQL, permitiendo la manipulación de los datos para excluir o agregar información según la necesidad.

¿Cómo activar la serialización en un proyecto?

Para usar la técnica de serialización, primero debes modificar el archivo main.typescript. Desde la librería @nestjs/common, junto con ValidationPipe, debes incluir un interceptor llamado ClassSerializerInterceptor. Posteriormente, configura la aplicación para que utilice este interceptor de manera global:

app.useGlobalInterceptors(
  new ClassSerializerInterceptor(app.get(Reflector))
);

Este paso inicial configura el proyecto para que pueda aplicar serialización en cualquier parte de la aplicación.

¿Cómo excluir información sensible?

La exclusión de datos es crucial, especialmente para información sensible como contraseñas. Usando ClassTransformer, puedes excluir campos como CREATED_AT e UPDATED_AT de las respuestas. A continuación, un ejemplo de cómo hacerlo en una entidad:

import { Exclude } from 'class-transformer';

class OrderItemEntity {
  @Exclude()
  createdAt: Date;

  @Exclude()
  updatedAt: Date;
}

Cuando el serializador está activo, estos campos se eliminan automáticamente de la respuesta, protegiendo así la información.

¿Cómo transformar o agregar nueva información?

Además de excluir datos, la serialización permite transformar los datos existentes o agregar nuevos campos con la ayuda del decorador @Expose. Por ejemplo, si necesitas un array de productos más limpio, como en el caso de una orden, puedes usar @Expose para crear un campo que procese y retorne la estructura deseada.

Ejemplo de transformación

import { Expose } from 'class-transformer';

class OrderEntity {
  private items: OrderItemEntity[];

  @Expose()
  get products() {
    return this.items?.filter(item => !!item)
      .map(item => ({
        ...item.product,
        quantity: item.quantity,
      })) || [];
  }
}

Este bloque de código asegura que sólo los datos necesarios, como los productos y su cantidad, se expongan de manera organizada.

Consideraciones al calcular campos dinámicamente

La técnica de serialización permite calcular campos dinámicamente. Sin embargo, es importante tener en cuenta el impacto en el rendimiento, especialmente cuando se trata de grandes volúmenes de datos. El cálculo de un total, por ejemplo, puede realizarse dentro de Node.js, pero para datos masivos es preferible delegar esos cálculos a una base de datos para evitar problemas de rendimiento.

Cálculo de un total de orden

Aquí un ejemplo de cómo implementar un cálculo de campo dinámico dentro de una entidad:

@Expose()
get total() {
  return this.items?.filter(item => !!item)
    .reduce((total, item) => total + item.product.price * item.quantity, 0) || 0;
}

Esta implementación depende de buenos hábitos de codificación y de optimización constante para asegurar que la API funcione de manera eficiente sin sobrecargar el servidor.


La serialización es una técnica poderosa cuando se aplica correctamente, asegurando que sólo la información relevante salga a la luz mientras mantiene la integridad y seguridad de los datos sensibles. ¡Practica implementando serialización en tus proyectos para profesionalizar la manipulación de datos en tus APIs!

Aportes 6

Preguntas 5

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Serializar

Esto sirve para transformar la información antes de retórnala

// src/main.ts

// Admitimos serialiacion
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));

Excluir un campo

Solo debemos agregarle el decorador @Exclude en el campo que queremos excluir

// src\users\entities\order-item.entity.ts

import { Exclude } from "class-transformer";
...
@Exclude()
@CreateDateColumn({
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  createAt: Date;

Transformar la informacion

// src\users\entities\order.entity.ts

@Expose()
  get products() {
    if (this.items) {
      return this.items
        .filter((item) => !!item)
        .map((item) => ({
          ...item.product,
          quantity: item.quantity,
          itemId: item.id,
        }));
    }
    return [];
  }

  @Expose()
  get total() {
    if (this.items) {
      return this.items
        .filter((item) => !!item)
        .reduce((total, item) => {
          const totalItem = item.product.price * item.quantity;
          return total + totalItem;
        }, 0);
    }
    return 0;
  }

Les dejo el enlace a la documentación sobre este tema: https://docs.nestjs.com/techniques/serialization

You are the best !!! 😉

Serializar

Serializar es transformar la información que el usuario nos esta enviando antes de que nuestro controlador la retorne al servicio.

Esto nos permite hacer una serie de cosas como:

  • Excluir información
  • Agregar información
  • Trastornarla a un formato deseado

Para hacer esto, vamos a tener que ir a nuestro **main.ts** y enviar la siguiente configuración:

// importamos el reflector
import { Reflector } from '@nestjs/core';
// importamos el interceptor
import { ClassSerializerInterceptor } from '@nestjs/common';

async function bootstrap() {
  // ...

  // usamos el interceptor en la configuración global
  // añadimos una nueva instancia de la clase "ClassSerializerInterceptor" a la configuración global
  // al crear la clase, le debemos mandar el reflector, para esto lo obtenemos y lo enviamos
  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
}
bootstrap();

Con esto, ya tenemos la configuración para utilizar los interceptors en toda nuestra aplicación, ¿Pero como lo hacemos? Veamos un ejemplo:

Supongamos que deseamos excluir cuando fue creado o actualizada una orden, ya que para el cliente puede ser innecesario verlo pero para la base de datos es importante tener un registro, ¿Cómo cambiaríamos esto? Para esto vamos a nuestra entidad de **order-item** y hagamos lo siguiente:

  • **src\users\entities\order-item.entity.ts**:
// de "class-transformer", nos vamos a tarer el decorador exclude
// "class-transformer" es quien nos da los decoradores para serializar la información
import { Exclude } from 'class-transformer';

@Entity({ name: 'order_items' })
export class OrderItem {
  // ...

  // excluímos el campo deseado cuando lo retornemos
  @Exclude()
  @CreateDateColumn({
    name: 'created_at',
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  createdAt: Date;

  // excluímos el campo deseado cuando lo retornemos
  @Exclude()
  @UpdateDateColumn({
    name: 'updated_at',
    type: 'timestamptz',
    default: () => 'CURRENT_TIMESTAMP',
  })
  updatedAt: Date;
}

Bien, ahora veamos como podemos modificar un dato antes de enviarlo. Digamos que deseamos transformar el Array de las ordenes antes de que lo enviemos, por ejemplo, si queremos hacer que el resultado de nuestra petición sea mucho más limpia podemos hacer que los items de la orden sean solo un array de productos y que nos dé el precio total de la orden de compra. Para realizar esto debemos utilizar el decorador **@Expose()**.

  • **src\users\entities\order.entity.ts**:
// importamos decoradores
import { Expose, Exclude } from 'class-transformer';

@Entity({ name: 'orders' })
export class Order {
  // ...

  // esto lo tenemos que excluír para que no duplique campos
  @Exclude()
  @OneToMany(() => OrderItem, (item) => item.order)
  items: OrderItem[];

  // decorador para "crear nuevos datos"
  @Expose()
  get products() {
    // tenemos items?
    if (this.items) {
      // hacemos esta transformación

      return this.items
        .filter((items) => !!items) // nos aseguramos que no sea nulo o oundefined
        .map((item) => ({
          // recorremos todos los items
          ...item,
          // le añadimos el campo "quantity" al producto
          quantity: item.quantity,
					// añadimos el identificador del item
          itemId: item.id,
        }));
    }

    return [];
  }

  // podemos crear un campo donde nos de el precio total de la orden
  @Expose()
  get total() {
    // tenemos items?
    if (this.items) {
      // hacemos esta transformación

      return this.items
        .filter((items) => !!items) // nos aseguramos que no sea nulo o oundefined
        .reduce((acum, item) => {
          // el precio total es igual a el precio por la cantidad
          const totalItem = item.product.price * item.quantity;
          // se lo sumamos al acum en cada iteración
          return acum + totalItem;
        }, 0);
    }

    return 0;
  }
}

Y listo, ya sabes usar una de las técnicas más útiles que tiene una API para manipular la información que le retorna al usuario. Sin embargo tienes que tener cuidado como manejas esta información porque hacer demasiados cálculos puede afectar negativamente el rendimiento de la app.

Uff, qué bello ese reduce 🗿 🚬

👏