Validación de datos con Zod en Nest JS

Nicolás Vega
Nicolás Vega
··

3 min de lectura

Cover Image for Validación de datos con Zod en Nest JS

Hoy vamos a charlar sobre algo súper importante en el mundo del desarrollo web con Nest JS: ¡la validación de datos usando Zod!

¿Qué es un Pipe en Nest JS?

Imagínense que están en una fábrica de chocolates. Los chocolates llegan por una cinta transportadora y pasan por varias máquinas que les hacen cosas: una les pone el papelito, otra les añade nueces... Bueno, en Nest JS, los "Pipes" son como esas máquinas. Cada vez que un request (una solicitud) llega a nuestro servidor, pasa por estos "Pipes" que hacen cosas con la data: la transforman, validan, o incluso pueden parar todo el proceso si algo no está bien.

Creando un Pipe de Zod

Zod es una herramienta genial para asegurarse de que los datos que recibimos son exactamente lo que esperamos. Así que, vamos a crear nuestro propio Pipe. Aquí les dejo un ejemplo:

zod-validation.pipe.ts

import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
import { ZodType, ZodError } from 'zod';

@Injectable()
export class ZodValidationPipe implements PipeTransform {
  constructor(private schema: ZodType) {}

  transform(value: any) {
    const result = this.schema.safeParse(value);
    if (!result.success) {
      const errorResult = result as { success: false; error: ZodError };
      throw new BadRequestException(
        `${JSON.stringify(errorResult.error.issues)}`,
        {
          cause: new Error(),
          description: 'ZOD_VALIDATION',
        },
      );
    }

    return result.data;
  }
}

¡Es como tener un guardián en la puerta de nuestro castillo de datos! Si algo no está bien, no pasa.

Creando un Esquema de Validación

Ahora, hablemos de cómo le decimos a Zod qué es lo que está bien y qué no. Es como darle un manual de instrucciones. Por ejemplo, si queremos validar los datos de un auto, podríamos hacer algo así:


export const createCarSchema = z.object({
  plate: z
    .string()
    .min(1, 'Debe ingresar una patente')
    .regex(plateRegexPattern, 'Debe ingresar una patente válido')
    .or(z.literal('NO DISPONIBLE')),
  color: z.union([z.string(), z.null()]).optional(),
  vim: z.union([z.string(), z.null()]).optional(),
  imageUrl: z.union([z.string(), z.null()]).optional(),
});

Aquí estamos diciendo: "Oye Zod, un auto debe tener una patente, y esa patente debe seguir un patrón específico. Y también, puede o no tener color, vim, imagen, etc."

schema

export const createCarSchema = z.object({
  plate: z
    .string()
    .min(1, 'Debe ingresar una patente')
    .regex(plateRegexPattern, 'Debe ingresar una patente válido')
    .or(z.literal('NO DISPONIBLE')),
  color: z.union([z.string(), z.null()]).optional(),
  vim: z.union([z.string(), z.null()]).optional(),
  imageUrl: z.union([z.string(), z.null()]).optional(),
  rtDue: z.union([z.string(), z.null()]).optional(),
  carVersion: createCarVersionSchema,
  carModel: createCarModelSchema,
  carBrand: createCarBrandSchema,
});

controller

Aplicando la Validación en el Controlador

Finalmente, ¿cómo usamos todo esto en la práctica? Simple, en nuestro controlador, cuando creamos un nuevo auto, le decimos a Nest JS que use nuestro Pipe de Zod para validar los datos:

@Post()
  create(
    @Body(new ZodValidationPipe(createCarSchema))
    createCarDto: CreateCarDto,
  ) {
    return this.carsService.create(createCarDto);
  }

Es como decir: "Antes de que hagas algo con estos datos, pásalos por el control de calidad de Zod".

¡Y eso es todo! Con Zod y Nest JS, pueden asegurarse de que los datos que manejan son tan precisos y seguros como un reloj suizo.