Overview

Validate request data with Zod schemas. Includes @ZodValidate decorator.

Features

  • @ZodValidate decorator
  • Detailed Zod errors
  • Per-route schemas
  • Type inference

Installation

npx hanma add zod-validation-pipe --framework nest

Dependencies

zod

Code

import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  BadRequestException,
  SetMetadata,
  UsePipes,
} from "@nestjs/common";
import { ZodSchema, ZodError, ZodIssue } from "zod";

export const ZOD_SCHEMA_KEY = "zod_schema";

export interface ZodValidationError {
  statusCode: 400;
  error: "Bad Request";
  message: string;
  errors: ZodFieldError[];
}

export interface ZodFieldError {
  field: string;
  message: string;
  code: string;
}

/**
 * Decorator to attach Zod schema to a route handler.
 * 
 * Usage:
 * @Post()
 * @ZodValidate(CreateUserSchema)
 * create(@Body() dto: CreateUserDto) {}
 */
export function ZodValidate(schema: ZodSchema) {
  return (target: any, key?: string, descriptor?: PropertyDescriptor) => {
    SetMetadata(ZOD_SCHEMA_KEY, schema)(target, key, descriptor);
    UsePipes(ZodValidationPipe)(target, key, descriptor);
  };
}

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

  transform(value: any, metadata: ArgumentMetadata) {
    if (!this.schema) {
      return value;
    }

    try {
      return this.schema.parse(value);
    } catch (error) {
      if (error instanceof ZodError) {
        throw new BadRequestException(this.formatZodError(error));
      }
      throw error;
    }
  }

  private formatZodError(error: ZodError): ZodValidationError {
    return {
      statusCode: 400,
      error: "Bad Request",
      message: "Validation failed",
      errors: error.issues.map((issue) => this.formatIssue(issue)),
    };
  }

  private formatIssue(issue: ZodIssue): ZodFieldError {
    return {
      field: issue.path.join("."),
      message: issue.message,
      code: issue.code,
    };
  }
}

/**
 * Factory function to create a pipe with a specific schema.
 * 
 * Usage:
 * @Post()
 * @UsePipes(zodPipe(CreateUserSchema))
 * create(@Body() dto: CreateUserDto) {}
 */
export function zodPipe(schema: ZodSchema) {
  return new ZodValidationPipe(schema);
}

// Example Zod schemas:
/*
import { z } from "zod";

export const CreateUserSchema = z.object({
  email: z.string().email("Invalid email format"),
  password: z.string().min(8, "Password must be at least 8 characters"),
  name: z.string().optional(),
  age: z.number().int().min(0).max(120).optional(),
  roles: z.array(z.enum(["user", "admin", "moderator"])).default(["user"]),
});

export type CreateUserDto = z.infer<typeof CreateUserSchema>;

// Nested schema
export const AddressSchema = z.object({
  street: z.string().min(1),
  city: z.string().min(1),
  zipCode: z.string().regex(/^\d{5}(-\d{4})?$/, "Invalid ZIP code"),
});

export const UserWithAddressSchema = CreateUserSchema.extend({
  address: AddressSchema.optional(),
});
*/

// Global pipe setup using Zod:
/*
import { INestApplication } from "@nestjs/common";
import { ZodValidationPipe } from "./pipes/zod-validation.pipe";
import { z } from "zod";

// Note: For global validation, you typically use class-validator
// Zod pipes are more commonly used per-route with @ZodValidate()
*/

Usage

@ZodValidate(CreateUserSchema)
@Post()
create(@Body() dto: CreateUserDto) {}

Dependencies

Frequently Asked Questions

How do I install Zod Validation Pipe?
Run the following command: npx hanma add zod-validation-pipe --framework nest
What dependencies are required?
This snippet requires: zod. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to src/pipes/zod-validation.pipe.ts.
Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.