Idea logo Idea

This tutorial demonstrates how to create a plugin that generates Zod validation schemas from .idea schema files. The plugin will transform your schema models into type-safe validation schemas with comprehensive validation rules.

  1. Overview
  2. Prerequisites
  3. Plugin Structure
  4. Implementation
  5. Schema Configuration
  6. Usage Examples
  7. Advanced Features
  8. Best Practices
  9. Troubleshooting

1. Overview

Zod is a TypeScript-first schema validation library that provides runtime type checking and validation. This plugin transforms your .idea schema definitions into comprehensive Zod validation schemas that provide robust runtime validation with excellent TypeScript integration.

This plugin generates Zod schemas from your .idea schema, including:

  • Schema Validation: Zod schemas for all models and types
  • Type Inference: TypeScript types inferred from Zod schemas
  • Custom Validators: Support for custom validation rules
  • Error Messages: Customizable validation error messages
  • Nested Validation: Support for nested objects and arrays

2. Prerequisites

Before implementing the Zod validation schema generator plugin, ensure you have the necessary development environment and knowledge. This section covers the essential requirements for successful plugin creation and Zod integration.

  • Node.js 16+ and npm/yarn
  • TypeScript 4.0+
  • Zod 3.0+
  • Basic understanding of validation concepts
  • Familiarity with the @stackpress/idea-transformer library
  • Understanding of .idea schema format

3. Plugin Structure

The plugin structure defines the core architecture and configuration interface for the Zod validation schema generator. This includes the main plugin function, configuration types, and the overall organization of the generated validation code.

import type { PluginProps } from '@stackpress/idea-transformer/types';
import fs from 'fs/promises';
import path from 'path';

interface ZodConfig {
  output: string;
  generateTypes?: boolean;
  includeEnums?: boolean;
  customValidators?: Record<string, string>;
  errorMessages?: Record<string, string>;
  strictMode?: boolean;
  exportStyle?: 'named' | 'default' | 'namespace';
}

export default async function generateZodSchemas(
  props: PluginProps<{ config: ZodConfig }>
) {
  const { config, schema, transformer } = props;
  
  // Implementation here...
}

4. Implementation

The implementation section covers the core plugin function and supporting utilities that handle Zod schema generation. This includes configuration validation, content generation, file writing, and error handling throughout the generation process.

4.1. Core Plugin Function

The core plugin function serves as the main entry point for Zod schema generation. It orchestrates the entire process from configuration validation through content generation to file output, ensuring proper error handling and logging throughout.

export default async function generateZodSchemas(
  props: PluginProps<{ config: ZodConfig }>
) {
  const { config, schema, transformer } = props;
  
  try {
    // Validate configuration
    validateConfig(config);
    
    // Generate Zod content
    let content = '';
    
    // Add file header and imports
    content += generateFileHeader();
    content += generateImports(config);
    
    // Generate enums if requested
    if (config.includeEnums && schema.enum) {
      content += generateEnumSchemas(schema.enum, config);
    }
    
    // Generate custom type schemas
    if (schema.type) {
      content += generateTypeSchemas(schema.type, config);
    }
    
    // Generate model schemas
    if (schema.model) {
      content += generateModelSchemas(schema.model, config);
    }
    
    // Generate utility schemas
    content += generateUtilitySchemas(schema, config);
    
    // Generate main export
    content += generateMainExport(schema, config);
    
    // Write to output file
    const outputPath = await transformer.loader.absolute(config.output);
    await fs.mkdir(path.dirname(outputPath), { recursive: true });
    await fs.writeFile(outputPath, content, 'utf8');
    
    console.log(`✅ Zod validation schemas generated: ${outputPath}`);
    
  } catch (error) {
    console.error('❌ Zod schema generation failed:', error.message);
    throw error;
  }
}

4.2. Generation Functions

Generation functions create specific parts of the Zod validation output including enum schemas, type schemas, model schemas, and utility schemas. These functions handle proper Zod syntax construction and validation rule application.

function generateFileHeader(): string {
  const timestamp = new Date().toISOString();
  return `/**
 * Generated Zod Validation Schemas
 * Generated at: ${timestamp}
 * 
 * This file is auto-generated. Do not edit manually.
 */

`;
}

function generateImports(config: ZodConfig): string {
  let imports = `import { z } from 'zod';\n\n`;
  
  if (config.generateTypes) {
    imports += `// Type inference helpers\ntype Infer<T> = z.infer<T>;\n\n`;
  }
  
  return imports;
}

function generateEnumSchemas(enums: Record<string, any>, config: ZodConfig): string {
  let content = '// Enum Schemas\n';
  
  for (const [enumName, enumDef] of Object.entries(enums)) {
    const values = Object.values(enumDef);
    const zodValues = values.map(v => `"${v}"`).join(', ');
    
    content += `export const ${enumName}Schema = z.enum([${zodValues}]);\n`;
    
    if (config.generateTypes) {
      content += `export type ${enumName} = z.infer<typeof ${enumName}Schema>;\n`;
    }
    
    content += '\n';
  }
  
  return content + '\n';
}

function generateTypeSchemas(types: Record<string, any>, config: ZodConfig): string {
  let content = '// Type Schemas\n';
  
  for (const [typeName, typeDef] of Object.entries(types)) {
    content += `export const ${typeName}Schema = z.object({\n`;
    
    for (const column of typeDef.columns || []) {
      const fieldSchema = generateFieldSchema(column, config);
      content += `  ${column.name}: ${fieldSchema},\n`;
    }
    
    content += '})';
    
    // Add strict mode if enabled
    if (config.strictMode) {
      content += '.strict()';
    }
    
    content += ';\n';
    
    if (config.generateTypes) {
      content += `export type ${typeName} = z.infer<typeof ${typeName}Schema>;\n`;
    }
    
    content += '\n';
  }
  
  return content;
}

function generateModelSchemas(models: Record<string, any>, config: ZodConfig): string {
  let content = '// Model Schemas\n';
  
  for (const [modelName, model] of Object.entries(models)) {
    content += `export const ${modelName}Schema = z.object({\n`;
    
    for (const column of model.columns || []) {
      const fieldSchema = generateFieldSchema(column, config);
      content += `  ${column.name}: ${fieldSchema},\n`;
    }
    
    content += '})';
    
    // Add strict mode if enabled
    if (config.strictMode) {
      content += '.strict()';
    }
    
    content += ';\n';
    
    if (config.generateTypes) {
      content += `export type ${modelName} = z.infer<typeof ${modelName}Schema>;\n`;
    }
    
    // Generate input schemas
    content += generateInputSchemas(modelName, model, config);
    
    content += '\n';
  }
  
  return content;
}

function generateFieldSchema(column: any, config: ZodConfig): string {
  let schema = mapTypeToZod(column.type, config);
  
  // Handle arrays
  if (column.multiple) {
    schema = `z.array(${schema})`;
  }
  
  // Handle optional fields
  if (!column.required) {
    schema += '.optional()';
  }
  
  // Add custom validations based on attributes
  if (column.attributes) {
    schema = addAttributeValidations(schema, column.attributes, config);
  }
  
  return schema;
}

function mapTypeToZod(schemaType: string, config: ZodConfig): string {
  // Check for custom validators first
  if (config.customValidators && config.customValidators[schemaType]) {
    return config.customValidators[schemaType];
  }
  
  const typeMap: Record<string, string> = {
    'String': 'z.string()',
    'Number': 'z.number()',
    'Integer': 'z.number().int()',
    'Boolean': 'z.boolean()',
    'Date': 'z.date()',
    'JSON': 'z.any()',
    'ID': 'z.string()'
  };
  
  return typeMap[schemaType] || `${schemaType}Schema`;
}

function addAttributeValidations(schema: string, attributes: any, config: ZodConfig): string {
  let validatedSchema = schema;
  
  // String validations
  if (attributes.min && schema.includes('z.string()')) {
    validatedSchema += `.min(${attributes.min})`;
  }
  
  if (attributes.max && schema.includes('z.string()')) {
    validatedSchema += `.max(${attributes.max})`;
  }
  
  if (attributes.email && schema.includes('z.string()')) {
    validatedSchema += '.email()';
  }
  
  if (attributes.url && schema.includes('z.string()')) {
    validatedSchema += '.url()';
  }
  
  if (attributes.uuid && schema.includes('z.string()')) {
    validatedSchema += '.uuid()';
  }
  
  if (attributes.regex && schema.includes('z.string()')) {
    validatedSchema += `.regex(/${attributes.regex}/)`;
  }
  
  // Number validations
  if (attributes.min && schema.includes('z.number()')) {
    validatedSchema += `.min(${attributes.min})`;
  }
  
  if (attributes.max && schema.includes('z.number()')) {
    validatedSchema += `.max(${attributes.max})`;
  }
  
  if (attributes.positive && schema.includes('z.number()')) {
    validatedSchema += '.positive()';
  }
  
  if (attributes.negative && schema.includes('z.number()')) {
    validatedSchema += '.negative()';
  }
  
  if (attributes.nonnegative && schema.includes('z.number()')) {
    validatedSchema += '.nonnegative()';
  }
  
  // Array validations
  if (attributes.minLength && schema.includes('z.array(')) {
    validatedSchema += `.min(${attributes.minLength})`;
  }
  
  if (attributes.maxLength && schema.includes('z.array(')) {
    validatedSchema += `.max(${attributes.maxLength})`;
  }
  
  if (attributes.nonempty && schema.includes('z.array(')) {
    validatedSchema += '.nonempty()';
  }
  
  // Custom error messages
  if (config.errorMessages) {
    const fieldName = extractFieldName(schema);
    if (fieldName && config.errorMessages[fieldName]) {
      validatedSchema += `.describe("${config.errorMessages[fieldName]}")`;
    }
  }
  
  return validatedSchema;
}

function extractFieldName(schema: string): string | null {
  // Extract field name from schema for error message lookup
  const match = schema.match(/(\w+)Schema/);
  return match ? match[1] : null;
}

function generateInputSchemas(modelName: string, model: any, config: ZodConfig): string {
  let content = '';
  
  // Generate create input schema (omit auto-generated fields)
  const createFields = model.columns?.filter((col: any) => 
    !col.attributes?.id && !col.attributes?.default
  ) || [];
  
  if (createFields.length > 0) {
    content += `export const Create${modelName}Schema = z.object({\n`;
    
    for (const column of createFields) {
      const fieldSchema = generateFieldSchema(column, config);
      content += `  ${column.name}: ${fieldSchema},\n`;
    }
    
    content += '})';
    
    if (config.strictMode) {
      content += '.strict()';
    }
    
    content += ';\n';
    
    if (config.generateTypes) {
      content += `export type Create${modelName}Input = z.infer<typeof Create${modelName}Schema>;\n`;
    }
  }
  
  // Generate update input schema (all fields optional)
  content += `export const Update${modelName}Schema = ${modelName}Schema.partial()`;
  
  if (config.strictMode) {
    content += '.strict()';
  }
  
  content += ';\n';
  
  if (config.generateTypes) {
    content += `export type Update${modelName}Input = z.infer<typeof Update${modelName}Schema>;\n`;
  }
  
  return content;
}

function generateUtilitySchemas(schema: any, config: ZodConfig): string {
  let content = '// Utility Schemas\n';
  
  // Generate pagination schema
  content += `export const PaginationSchema = z.object({
  page: z.number().int().positive().default(1),
  limit: z.number().int().positive().max(100).default(10),
  search: z.string().optional(),
  sort: z.string().optional(),
});

`;
  
  if (config.generateTypes) {
    content += `export type PaginationParams = z.infer<typeof PaginationSchema>;\n\n`;
  }
  
  // Generate API response schemas
  content += `export const APIResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
  z.object({
    success: z.boolean(),
    data: dataSchema.optional(),
    error: z.string().optional(),
    errors: z.record(z.array(z.string())).optional(),
  });

export const PaginatedResponseSchema = <T extends z.ZodTypeAny>(dataSchema: T) =>
  z.object({
    success: z.boolean(),
    data: z.array(dataSchema).optional(),
    total: z.number().int().nonnegative(),
    page: z.number().int().positive(),
    limit: z.number().int().positive(),
    error: z.string().optional(),
    errors: z.record(z.array(z.string())).optional(),
  });

`;
  
  return content;
}

function generateMainExport(schema: any, config: ZodConfig): string {
  if (config.exportStyle === 'namespace') {
    return generateNamespaceExport(schema, config);
  }
  
  if (config.exportStyle === 'default') {
    return generateDefaultExport(schema, config);
  }
  
  // Named exports (default)
  return '// All schemas are exported as named exports above\n';
}

function generateNamespaceExport(schema: any, config: ZodConfig): string {
  let content = 'export namespace Schemas {\n';
  
  // Export enums
  if (config.includeEnums && schema.enum) {
    for (const enumName of Object.keys(schema.enum)) {
      content += `  export const ${enumName} = ${enumName}Schema;\n`;
    }
  }
  
  // Export types
  if (schema.type) {
    for (const typeName of Object.keys(schema.type)) {
      content += `  export const ${typeName} = ${typeName}Schema;\n`;
    }
  }
  
  // Export models
  if (schema.model) {
    for (const modelName of Object.keys(schema.model)) {
      content += `  export const ${modelName} = ${modelName}Schema;\n`;
      content += `  export const Create${modelName} = Create${modelName}Schema;\n`;
      content += `  export const Update${modelName} = Update${modelName}Schema;\n`;
    }
  }
  
  content += '}\n\n';
  return content;
}

function generateDefaultExport(schema: any, config: ZodConfig): string {
  let content = 'const schemas = {\n';
  
  // Add enums
  if (config.includeEnums && schema.enum) {
    for (const enumName of Object.keys(schema.enum)) {
      content += `  ${enumName}: ${enumName}Schema,\n`;
    }
  }
  
  // Add types
  if (schema.type) {
    for (const typeName of Object.keys(schema.type)) {
      content += `  ${typeName}: ${typeName}Schema,\n`;
    }
  }
  
  // Add models
  if (schema.model) {
    for (const modelName of Object.keys(schema.model)) {
      content += `  ${modelName}: ${modelName}Schema,\n`;
      content += `  Create${modelName}: Create${modelName}Schema,\n`;
      content += `  Update${modelName}: Update${modelName}Schema,\n`;
    }
  }
  
  content += `  Pagination: PaginationSchema,
  APIResponse: APIResponseSchema,
  PaginatedResponse: PaginatedResponseSchema,
};

export default schemas;
`;
  
  return content;
}

function validateConfig(config: any): asserts config is ZodConfig {
  if (!config.output || typeof config.output !== 'string') {
    throw new Error('Zod plugin requires "output" configuration as string');
  }
  
  if (config.exportStyle && !['named', 'default', 'namespace'].includes(config.exportStyle)) {
    throw new Error('exportStyle must be one of: named, default, namespace');
  }
}

5. Schema Configuration

Schema configuration demonstrates how to integrate the Zod validation generator into your .idea schema files. This section covers plugin configuration options and their effects on the generated validation schemas.

Add the Zod validation plugin to your .idea schema file:

plugin "./plugins/zod-validation.js" {
  output "./generated/validation.ts"
  generateTypes true
  includeEnums true
  strictMode true
  exportStyle "named"
  customValidators {
    Email "z.string().email()"
    Password "z.string().min(8).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)/)"
    PhoneNumber "z.string().regex(/^\\+?[1-9]\\d{1,14}$/)"
  }
  errorMessages {
    email "Please enter a valid email address"
    password "Password must be at least 8 characters with uppercase, lowercase, and number"
    required "This field is required"
  }
}

Configuration Options

Configuration options control how Zod validation schemas are generated, including output formatting, validation strictness, and feature enablement. Understanding these options helps you customize the plugin to meet your specific validation requirements.

OptionTypeDefaultDescription
generateTypesbooleantrueGenerate TypeScript types from schemas
includeEnumsbooleantrueGenerate enum validation schemas
customValidatorsobject{}Custom Zod validators for specific types
errorMessagesobject{}Custom error messages for validation
strictModebooleanfalseUse strict object validation
exportStyle`'named'\'default'\'namespace'`'named'Export style for schemas

6. Usage Examples

Usage examples demonstrate practical applications of the Zod validation generator with real-world scenarios. These examples show how to configure the plugin for different use cases and how the generated validation schemas integrate into development workflows.

6.1. Basic Schema

A basic schema example shows the fundamental structure needed to generate Zod validation schemas. This includes model definitions with proper validation attributes and plugin configuration that produces comprehensive validation rules.

enum UserRole {
  ADMIN "admin"
  USER "user"
  GUEST "guest"
}

model User {
  id String @id @default("nanoid()")
  email String @email @required
  name String @min(2) @max(50) @required
  age Number @min(18) @max(120)
  role UserRole @default("USER")
  active Boolean @default(true)
  createdAt Date @default("now()")
}

plugin "./plugins/zod-validation.js" {
  output "./validation.ts"
  generateTypes true
  strictMode true
}

6.2. Generated Validation Usage

The generated validation usage demonstrates how to use the Zod schemas produced by the plugin in real applications. This shows practical patterns for data validation, error handling, and type safety in TypeScript applications.

import { 
  UserSchema, 
  CreateUserSchema, 
  UpdateUserSchema,
  UserRole 
} from './validation';

// Validate complete user object
const validateUser = (data: unknown) => {
  try {
    const user = UserSchema.parse(data);
    console.log('Valid user:', user);
    return { success: true, data: user };
  } catch (error) {
    console.error('Validation failed:', error.errors);
    return { success: false, errors: error.errors };
  }
};

// Validate user creation data
const validateCreateUser = (data: unknown) => {
  const result = CreateUserSchema.safeParse(data);
  
  if (result.success) {
    console.log('Valid create data:', result.data);
    return result.data;
  } else {
    console.error('Validation errors:', result.error.errors);
    throw new Error('Invalid user data');
  }
};

// Validate user update data
const validateUpdateUser = (data: unknown) => {
  return UpdateUserSchema.parse(data);
};

// Example usage
const userData = {
  email: '[email protected]',
  name: 'John Doe',
  age: 30,
  role: 'user' as UserRole
};

const validUser = validateCreateUser(userData);

6.3. Form Validation Example

Form validation examples show how to integrate the generated Zod schemas with frontend frameworks for user input validation. This demonstrates real-world usage patterns for form handling and user feedback.

import { CreateUserSchema } from './validation';

// React form validation
function UserForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});
  
  const handleSubmit = (formData: FormData) => {
    const data = Object.fromEntries(formData);
    
    const result = CreateUserSchema.safeParse(data);
    
    if (!result.success) {
      const fieldErrors: Record<string, string> = {};
      
      result.error.errors.forEach((error) => {
        const field = error.path[0] as string;
        fieldErrors[field] = error.message;
      });
      
      setErrors(fieldErrors);
      return;
    }
    
    // Submit valid data
    submitUser(result.data);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" />
      {errors.email && <span>{errors.email}</span>}
      
      <input name="name" type="text" />
      {errors.name && <span>{errors.name}</span>}
      
      <input name="age" type="number" />
      {errors.age && <span>{errors.age}</span>}
      
      <button type="submit">Create User</button>
    </form>
  );
}

7. Advanced Features

Advanced features extend the basic Zod schema generation with sophisticated validation patterns, conditional logic, data transformation, and asynchronous validation. These features enable production-ready validation that handles complex business requirements.

7.1. Custom Validators

Custom validators allow you to define specialized validation logic for specific data types or business rules. This feature enables the creation of reusable validation patterns that can be applied across multiple schema definitions.

// In plugin configuration
customValidators: {
  Email: "z.string().email().transform(val => val.toLowerCase())",
  Password: "z.string().min(8).regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)/)",
  Slug: "z.string().regex(/^[a-z0-9-]+$/)",
  Color: "z.string().regex(/^#[0-9A-F]{6}$/i)",
  JSON: "z.string().transform(val => JSON.parse(val))"
}

7.2. Conditional Validation

Conditional validation enables complex validation logic that depends on the values of other fields. This feature is essential for implementing business rules that require cross-field validation and context-dependent constraints.

// Generated schema with conditional validation
export const UserSchema = z.object({
  id: z.string(),
  email: z.string().email(),
  role: UserRoleSchema,
  adminCode: z.string().optional(),
}).refine((data) => {
  // Admin users must have admin code
  if (data.role === 'admin' && !data.adminCode) {
    return false;
  }
  return true;
}, {
  message: "Admin users must provide an admin code",
  path: ["adminCode"],
});

7.3. Transform and Preprocess

Transform and preprocess capabilities allow you to modify data during validation, enabling data normalization, formatting, and cleanup. This feature ensures data consistency and proper formatting before validation.

// Add transforms to generated schemas
function addTransforms(schema: string, column: any): string {
  if (column.attributes?.transform) {
    switch (column.attributes.transform) {
      case 'lowercase':
        return schema + '.transform(val => val.toLowerCase())';
      case 'uppercase':
        return schema + '.transform(val => val.toUpperCase())';
      case 'trim':
        return schema + '.transform(val => val.trim())';
      case 'slug':
        return schema + '.transform(val => val.toLowerCase().replace(/\\s+/g, "-"))';
    }
  }
  
  return schema;
}

7.4. Async Validation

Async validation enables validation rules that require external data sources or API calls, such as checking for unique values or validating against external services. This feature is crucial for comprehensive data validation in real applications.

// Generate async validation schemas
export const UserSchemaAsync = UserSchema.extend({
  email: z.string().email().refine(async (email) => {
    // Check if email is unique
    const exists = await checkEmailExists(email);
    return !exists;
  }, {
    message: "Email already exists",
  }),
});

// Usage
const validateUserAsync = async (data: unknown) => {
  try {
    const user = await UserSchemaAsync.parseAsync(data);
    return { success: true, data: user };
  } catch (error) {
    return { success: false, errors: error.errors };
  }
};

8. Best Practices

Best practices ensure your generated Zod validation schemas are maintainable, performant, and provide excellent developer experience. These guidelines cover error handling, schema composition, type safety, and API integration patterns.

8.1. Error Handling

Proper error handling ensures that validation failures provide clear, actionable feedback to users and developers. Implement centralized error handling patterns and meaningful error messages to improve the overall user experience.

// Centralized validation error handling
class ValidationError extends Error {
  constructor(public errors: z.ZodError) {
    super('Validation failed');
    this.name = 'ValidationError';
  }
  
  getFieldErrors(): Record<string, string> {
    const fieldErrors: Record<string, string> = {};
    
    this.errors.errors.forEach((error) => {
      const field = error.path.join('.');
      fieldErrors[field] = error.message;
    });
    
    return fieldErrors;
  }
}

// Usage
function validateWithErrorHandling<T>(schema: z.ZodSchema<T>, data: unknown): T {
  const result = schema.safeParse(data);
  
  if (!result.success) {
    throw new ValidationError(result.error);
  }
  
  return result.data;
}

8.2. Schema Composition

Schema composition enables the creation of reusable validation components that can be combined to build complex validation schemas. This approach promotes code reuse and maintains consistency across your validation logic.

// Compose schemas for reusability
const BaseEntitySchema = z.object({
  id: z.string(),
  createdAt: z.date(),
  updatedAt: z.date(),
});

const UserSchema = BaseEntitySchema.extend({
  email: z.string().email(),
  name: z.string().min(1),
});

const PostSchema = BaseEntitySchema.extend({
  title: z.string().min(1),
  content: z.string(),
  authorId: z.string(),
});

8.3. Type Guards

Type guards provide runtime type checking that integrates seamlessly with TypeScript's type system. Generated type guards enable safe type narrowing and improve code reliability by ensuring data conforms to expected types.

// Generate type guards from schemas
export const isUser = (data: unknown): data is User => {
  return UserSchema.safeParse(data).success;
};

export const isCreateUserInput = (data: unknown): data is CreateUserInput => {
  return CreateUserSchema.safeParse(data).success;
};

// Usage
function processUserData(data: unknown) {
  if (isUser(data)) {
    // TypeScript knows data is User
    console.log(data.email);
  }
}

8.4. API Integration

API integration patterns show how to use generated Zod schemas for request validation in web applications. This includes middleware creation, error response formatting, and type-safe request handling.

// Middleware for API validation
function validateBody<T>(schema: z.ZodSchema<T>) {
  return (req: Request, res: Response, next: NextFunction) => {
    const result = schema.safeParse(req.body);
    
    if (!result.success) {
      return res.status(400).json({
        success: false,
        error: 'Validation failed',
        errors: result.error.errors.reduce((acc, err) => {
          const field = err.path.join('.');
          acc[field] = err.message;
          return acc;
        }, {} as Record<string, string>)
      });
    }
    
    req.body = result.data;
    next();
  };
}

// Usage
app.post('/users', validateBody(CreateUserSchema), (req, res) => {
  // req.body is now typed and validated
  const user = req.body; // Type: CreateUserInput
});

9. Troubleshooting

This section addresses common issues encountered when generating Zod validation schemas and provides solutions for debugging and resolving problems. Understanding these troubleshooting techniques helps ensure reliable validation schema generation.

9.1. Common Issues

Common issues include circular dependencies, complex validation rules, and performance problems with large schemas. These problems typically arise from schema complexity or validation requirements that need specialized handling.

9.1.1. Circular Dependencies

Circular dependencies occur when schemas reference each other in a way that creates infinite loops. Zod provides lazy evaluation to handle these scenarios while maintaining type safety and validation integrity.

   // Handle circular references with lazy evaluation
   const UserSchema: z.ZodSchema<User> = z.lazy(() => z.object({
     id: z.string(),
     posts: z.array(PostSchema),
   }));
   
   const PostSchema: z.ZodSchema<Post> = z.lazy(() => z.object({
     id: z.string(),
     author: UserSchema,
   }));

9.1.2. Complex Validation Rules

Complex validation rules require sophisticated logic that goes beyond simple type checking. Use Zod's refinement capabilities to implement business rules and cross-field validation while maintaining clear error messages.

   // Use refinements for complex validation
   const PasswordSchema = z.string()
     .min(8, "Password must be at least 8 characters")
     .refine((password) => /[A-Z]/.test(password), {
       message: "Password must contain at least one uppercase letter",
     })
     .refine((password) => /[a-z]/.test(password), {
       message: "Password must contain at least one lowercase letter",
     })
     .refine((password) => /\d/.test(password), {
       message: "Password must contain at least one number",
     });

9.1.3. Performance Issues

Performance issues can arise when validation schemas are complex or when processing large amounts of data. Optimize validation performance using preprocessing, caching, and efficient schema design patterns.

   // Use preprocess for expensive operations
   const OptimizedSchema = z.preprocess(
     (data) => {
       // Expensive preprocessing
       return normalizeData(data);
     },
     z.object({
       // Schema definition
     })
   );

9.2. Debugging Tips

Debugging tips help identify and resolve issues during Zod schema generation and usage. These techniques provide visibility into validation behavior and help diagnose problems with schema logic or performance.

9.2.1. Schema Testing

Schema testing ensures that your validation logic works correctly across different input scenarios. Comprehensive testing helps catch edge cases and ensures validation behaves as expected in production.

   // Test schemas with various inputs
   describe('UserSchema', () => {
     it('should validate valid user data', () => {
       const validData = {
         email: '[email protected]',
         name: 'Test User',
         age: 25
       };
       
       expect(() => UserSchema.parse(validData)).not.toThrow();
     });
     
     it('should reject invalid email', () => {
       const invalidData = {
         email: 'invalid-email',
         name: 'Test User',
         age: 25
       };
       
       expect(() => UserSchema.parse(invalidData)).toThrow();
     });
   });

9.2.2. Error Message Customization

Error message customization improves user experience by providing clear, actionable feedback when validation fails. Well-crafted error messages help users understand what went wrong and how to fix it.

   // Customize error messages for better UX
   const UserSchema = z.object({
     email: z.string().email("Please enter a valid email address"),
     age: z.number().min(18, "You must be at least 18 years old"),
   });

This tutorial provides a comprehensive foundation for creating Zod validation schemas from .idea files. The generated schemas provide runtime type checking and validation with excellent TypeScript integration.