Claude
Skills
Sign in
Back

zod

Included with Lifetime
$97 forever

A Zod v4 validation specialist.

General

What this skill does


# Zod

This skill provides guidance for implementing type-safe validation using Zod v4 in TypeScript applications. It covers schema design, error handling, type inference, and migration from Zod 3.

## Zod 4 Requirements

This skill is exclusively for **Zod 4**, which introduced breaking changes from Zod 3. All examples and recommendations use Zod 4 syntax.

### Installation

```bash
npm install zod@^4.0.0
```

### Critical Zod 4 Changes

If you encounter Zod 3 code or examples, be aware of these breaking changes:

**Error Customization - Use `error` not `message`**

```typescript
z.string().min(5, { error: 'Too short.' });
z.string().min(5, { message: 'Too short.' });
```

**String Formats - Use top-level functions**

```typescript
z.email();
z.uuid();
z.url();
z.iso.date();
z.string().email();
```

**Object Methods - Use dedicated functions**

```typescript
z.strictObject({ name: z.string() });
z.looseObject({ name: z.string() });
z.object({ name: z.string() }).strict();
z.object({ name: z.string() }).passthrough();
```

**Error Formatting - Use top-level functions**

```typescript
z.flattenError(error);
z.treeifyError(error);
z.prettifyError(error);
error.flatten();
error.format();
```

**Function Schemas - New syntax**

```typescript
const myFn = z.function({
  input: [z.string()],
  output: z.number(),
});
const myFn = z.function().args(z.string()).returns(z.number());
```

**Enums - Unified API**

```typescript
enum Color {
  Red = 'red',
  Green = 'green',
}
z.enum(Color);
z.nativeEnum(Color);
```

**Deprecated APIs to avoid:**

- `invalid_type_error` and `required_error` parameters (use `error` function instead)
- `.merge()` on objects (use `.extend()` or object spread)
- `.deepPartial()` (removed, anti-pattern)
- `z.promise()` (rarely needed, just await the promise)
- Single-argument `z.record()` (now requires both key and value schemas)

### Key Improvements in Zod 4

- **Performance**: Dramatically faster parsing and validation
- **Error handling**: Unified `error` parameter for all error customization
- **Type safety**: Better TypeScript inference and type narrowing
- **Tree-shaking**: Top-level functions are more tree-shakable
- **Refinements**: Now stored inside schemas, not wrapper classes
- **Defaults in optional fields**: Applied correctly within optional properties

## Core Principles

1. Follow coding guidelines strictly (SRP, single export per file, prefer interfaces over types)
2. Create properly structured schema files with kebab-case naming
3. Leverage Zod's type inference for compile-time type safety
4. Implement proper error handling with safeParse for user-facing validations
5. Use refinements for custom validation logic
6. Organize schemas in a dedicated schemas directory structure

## Common Use Cases

### 1. API Request/Response Validation

When implementing API validation:

- Create schema files in `src/schemas/` or appropriate directory
- Export a single schema per file using kebab-case naming
- Use `.safeParse()` for user input to handle errors gracefully
- Use `.parse()` only when input is guaranteed to be valid
- Infer TypeScript types from schemas using `z.infer<>`

Example structure:

```
src/
  schemas/
    user-create-request.schema.ts
    user-response.schema.ts
  interfaces/
    user.interface.ts
  functions/
    validate-user-request.ts
```

### 2. Form Validation

For form validation:

- Create schemas matching form structure
- Use `.safeParse()` to validate on submit
- Extract and display field-specific errors using `z.flattenError()`
- Implement real-time validation with debouncing if needed
- Use refinements for cross-field validation (e.g., password confirmation)

### 3. Environment Variable Validation

For environment validation:

- Create a schema in `src/schemas/environment.schema.ts`
- Validate on application startup using `.parse()`
- Let it throw if environment is invalid (fail-fast approach)
- Export inferred type for use throughout the application

### 4. Schema Organization

Follow this structure:

- Simple schemas: Direct export from schema file
- Complex schemas: Build from smaller schemas
- Shared schemas: Create base schemas and extend/pick as needed
- Keep one schema export per file (following SRP)

### 5. Type Inference and Interfaces

- Use `z.infer<typeof Schema>` to extract types
- Save inferred types as interfaces in the interfaces directory
- Use interfaces instead of inline types throughout the codebase
- Export one interface per file with kebab-case naming

### 6. Error Handling Patterns

Implement these patterns:

- **User input**: Use `.safeParse()` and handle errors gracefully
- **Internal validation**: Use `.parse()` to fail fast on programming errors
- **Async validation**: Use `.parseAsync()` or `.safeParseAsync()` with async validations
- **Custom errors**: Use the `error` parameter for user-friendly messages
- **Formatted errors**: Use `z.flattenError()` for forms, `z.treeifyError()` for nested data

### 7. Understanding Error Structure

When validation fails, Zod returns a `ZodError` instance containing an `.issues` array. Each issue provides granular information about what went wrong.

#### The Issues Array

Every validation error contains detailed metadata:

```typescript
const result = schema.safeParse(invalidData);

if (!result.success) {
  result.error.issues;
}
```

Each issue object contains:

- **code**: Error code indicating the type of validation failure

  - `invalid_type`: Wrong data type (e.g., expected string, got number)
  - `custom`: Custom validation from `.refine()` or `.superRefine()`
  - `too_big`: Value exceeds maximum constraint
  - `too_small`: Value below minimum constraint
  - `unrecognized_keys`: Extra keys in strict objects
  - `invalid_string`: String format validation failed (email, url, etc.)
  - And many more specific codes

- **path**: Array showing the location of the error in nested structures

  - `[]` for top-level errors
  - `['username']` for object property errors
  - `['users', 0, 'email']` for nested array/object errors

- **message**: Human-readable error description

- **Context-specific properties** depending on error type:
  - `expected` and `received` for type mismatches
  - `minimum` and `maximum` for size constraints
  - `inclusive` for whether constraints are inclusive
  - `keys` for unrecognized keys
  - `validation` for string format types

#### Working with Issues Directly

Access the raw issues array for maximum control:

```typescript
const result = UserSchema.safeParse(data);

if (!result.success) {
  result.error.issues.forEach((issue) => {
    console.log(`Error at ${issue.path.join('.')}: ${issue.message}`);
    console.log(`Error code: ${issue.code}`);

    if (issue.code === 'invalid_type') {
      console.log(`Expected ${issue.expected}, got ${issue.received}`);
    }
  });
}
```

#### Error Formatting Utilities

Instead of manually processing issues, use Zod's formatting utilities:

**For flat forms** (single level):

```typescript
const flattened = z.flattenError(result.error);

flattened.formErrors;
flattened.fieldErrors.username;
flattened.fieldErrors.email;
```

**For nested structures**:

```typescript
const tree = z.treeifyError(result.error);

tree.errors;
tree.properties?.username?.errors;
tree.properties?.favoriteNumbers?.items?.[1]?.errors;
```

**For debugging**:

```typescript
const pretty = z.prettifyError(result.error);
console.log(pretty);
```

#### Common Error Response Patterns

**API Response with Issues**:

```typescript
export const handleValidationError = (error: z.ZodError) => {
  return {
    success: false,
    errors: error.issues.map((issue) => ({
      field: issue.path.join('.'),
      message: issue.message,
      code: issue.code,
    })),
  };
};
```

**Form Field Errors**:

```typescript
export const getFieldErrors = (error: z.ZodError) => {
  const formatted = z.flattenError(error);

  return {
    formErrors: formatted.formErrors,
    fieldErrors: formatted.fieldErrors,
  };
};
Files: 1
Size: 26.4 KB
Complexity: 28/100
Category: General

Related in General