> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/colinhacks/zod/llms.txt
> Use this file to discover all available pages before exploring further.

# .pipe()

> Chain schemas together for sequential validation and transformation

## Overview

The `.pipe()` method allows you to chain two schemas together, where the output of the first schema becomes the input of the second. This is useful for multi-stage validation and transformation pipelines.

## Signature

```typescript theme={null}
schema.pipe(targetSchema)
```

<ParamField path="targetSchema" type="ZodType" required>
  The schema to pipe into. Its input type must match the output type of the source schema.
</ParamField>

**Returns:** `ZodPipe<A, B>` where `A` is the source schema and `B` is the target schema

## Basic Usage

### String to Number Pipeline

The most common use case is converting and validating data in stages:

```typescript theme={null}
const schema = z.string()
  .transform(Number)
  .pipe(z.number().positive());

schema.parse("42");    // 42
schema.parse("-5");    // Error: Number must be positive
schema.parse("hello"); // Error: Invalid number
```

### Async Transformations

Pipes work with async transformations:

```typescript theme={null}
const schema = z.string()
  .transform(async (val) => Number(val))
  .pipe(z.number().int());

await schema.parseAsync("42"); // 42
```

## Advanced Patterns

### Multi-Stage Validation

Use pipes to validate at different stages of transformation:

```typescript theme={null}
const schema = z.string()
  .min(1, "Input cannot be empty")
  .transform((val) => val.trim())
  .pipe(
    z.string()
      .email("Must be a valid email")
      .transform((email) => email.toLowerCase())
  );

schema.parse("  User@Example.COM  "); // "user@example.com"
```

### Conditional Pipelines

Combine pipes with refinements for conditional logic:

```typescript theme={null}
const schema = z
  .pipe(
    z.transform((v) => v === "none" ? undefined : v),
    z.string()
  )
  .catch("default");

schema.parse("ok");    // "ok"
schema.parse("none");  // "default" (transformed to undefined, caught)
schema.parse(15);      // "default" (invalid type, caught)
```

### Refinement Before Transformation

Validate before transforming to prevent invalid transformations:

```typescript theme={null}
const schema = z.string()
  .refine((c) => c === "1234", "Must be 1234")
  .transform((val) => Number(val))
  .refine((c) => c === 1234, "Result must be 1234");

schema.parse("1234"); // 1234

const result = schema.safeParse("4321");
// Error at first refinement, transformation never runs
```

## Error Handling

### Non-Fatal Errors

By default, refinement errors are non-fatal and allow subsequent checks:

```typescript theme={null}
const schema = z.string()
  .refine((c) => c.length > 5, "Too short")
  .transform((val) => val.toUpperCase())
  .refine((c) => c.startsWith("A"), "Must start with A");

const result = schema.safeParse("abc");
// Both refinement errors are reported:
// - "Too short"
// - (transformation runs anyway)
// - "Must start with A"
```

### Fatal Errors

Use `abort: true` to stop validation on error:

```typescript theme={null}
const schema = z.string()
  .refine((c) => c === "1234", { 
    message: "Must be 1234", 
    abort: true 
  })
  .transform((val) => Number(val))
  .refine((c) => c === 1234, "Result must be 1234");

const result = schema.safeParse("4321");
// Only first error reported, transformation and second refinement are skipped
```

## Bidirectional Validation

### Encoding and Decoding

Pipes support both forward (decode) and reverse (encode) operations:

```typescript theme={null}
const schema = z.string().pipe(z.string());

// Forward: parse/decode
z.decode(schema, "hello");  // "hello"
z.parse(schema, "hello");   // "hello"

// Reverse: encode
z.encode(schema, "hello");  // "hello"
```

### Transform Encoding Errors

Unidirectional transforms cannot be encoded:

```typescript theme={null}
const schema = z.string().transform((val) => val.length);

try {
  z.encode(schema, 1234);
} catch (error) {
  console.log(error.message);
  // "Encountered unidirectional transform during encode: ZodTransform"
}
```

## Type Safety

Pipes maintain type safety throughout the chain:

```typescript theme={null}
const schema = z.string()
  .transform((s) => s.length)
  .pipe(z.number().positive());

type Input = z.input<typeof schema>;   // string
type Output = z.output<typeof schema>; // number

// TypeScript enforces correct types
const result: number = schema.parse("hello"); // 5
```

### Type Mismatch Errors

TypeScript will catch type mismatches between piped schemas:

```typescript theme={null}
// ❌ Type error: number is not assignable to string
const invalid = z.number()
  .pipe(z.string()); // Error!

// ✅ Correct: transform to match next schema's input
const valid = z.number()
  .transform(String)
  .pipe(z.string());
```

## Comparison with Transform

| Feature     | `.pipe()`                      | `.transform()`             |
| ----------- | ------------------------------ | -------------------------- |
| Purpose     | Chain complete schemas         | Transform within schema    |
| Validation  | Validates at each stage        | Validates before transform |
| Reversible  | Can be reversible (with codec) | One-way only               |
| Type safety | Strict input/output matching   | Flexible                   |
| Use case    | Multi-stage pipelines          | Single transformation      |

```typescript theme={null}
// Using pipe (multi-stage)
const piped = z.string()
  .transform(Number)
  .pipe(
    z.number()
      .int()
      .positive()
  );

// Using transform only (single stage)
const transformed = z.string()
  .transform((s) => {
    const num = Number(s);
    if (!Number.isInteger(num) || num <= 0) {
      throw new Error("Invalid");
    }
    return num;
  });
```

## Common Patterns

### String Preprocessing

Clean and validate string input:

```typescript theme={null}
const emailSchema = z.string()
  .transform((s) => s.trim().toLowerCase())
  .pipe(z.string().email());

emailSchema.parse("  USER@EXAMPLE.COM  "); // "user@example.com"
```

### JSON Parsing

Parse and validate JSON strings:

```typescript theme={null}
const jsonSchema = z.string()
  .transform((str) => JSON.parse(str))
  .pipe(
    z.object({
      id: z.number(),
      name: z.string(),
    })
  );

const data = jsonSchema.parse('{"id": 1, "name": "Alice"}');
// { id: 1, name: "Alice" }
```

### Date Normalization

```typescript theme={null}
const dateSchema = z.string()
  .transform((s) => new Date(s))
  .pipe(
    z.date()
      .min(new Date("2020-01-01"))
      .max(new Date())
  );

dateSchema.parse("2023-06-15"); // Date object
```

### Form Value Processing

```typescript theme={null}
const formValueSchema = z.string()
  .transform((val) => val === "" ? undefined : val)
  .pipe(z.string().min(3).optional());

formValueSchema.parse("");      // undefined
formValueSchema.parse("hello"); // "hello"
```

## Best Practices

<Card title="Validate early">
  Place validation as early as possible in the pipeline to catch errors before expensive transformations.
</Card>

<Card title="Use meaningful error messages">
  Add custom error messages at each stage to help identify where validation failed.
</Card>

<Card title="Consider performance">
  Each pipe stage adds overhead. For simple cases, a single transform might be more efficient.
</Card>

<Card title="Type safety first">
  Let TypeScript guide you - if types don't match, add explicit transforms to bridge the gap.
</Card>

## Error Examples

### Type Mismatch

```typescript theme={null}
const schema = z.string()
  .transform(Number)
  .pipe(z.number());

const result = schema.safeParse("not a number");

if (!result.success) {
  console.log(result.error.issues);
  // [
  //   {
  //     code: "invalid_type",
  //     expected: "number",
  //     received: "nan",
  //     path: [],
  //     message: "Invalid input: expected number, received nan"
  //   }
  // ]
}
```

### Validation Failure

```typescript theme={null}
const schema = z.string()
  .transform(Number)
  .pipe(z.number().int().positive());

const result = schema.safeParse("-5");

if (!result.success) {
  console.log(result.error.issues);
  // [
  //   {
  //     code: "too_small",
  //     minimum: 0,
  //     inclusive: false,
  //     path: [],
  //     message: "Number must be greater than 0"
  //   }
  // ]
}
```

## See Also

* [Transform](/api/utilities/transform) - Data transformation
* [Refine](/api/utilities/refine) - Custom validation
* [Coerce](/api/utilities/coerce) - Type coercion
* [Codec](/api/utilities/codec) - Bidirectional transformations
