0
# Transformations and Pipelines
1
2
Transform validated data or chain schemas together with type safety.
3
4
## Transform Method
5
6
```typescript { .api }
7
interface ZodType<Output, Input> {
8
transform<T>(fn: (val: Output) => T | Promise<T>): ZodEffects<this, T>;
9
}
10
```
11
12
**Examples:**
13
```typescript
14
// Basic transformation
15
z.string().transform((val) => val.toUpperCase())
16
z.string().transform((val) => val.length)
17
z.string().transform((val) => parseInt(val))
18
19
// Async transformation
20
z.string().transform(async (val) => {
21
const response = await fetch(`/api/${val}`);
22
return response.json();
23
})
24
// Requires: await schema.parseAsync(data);
25
26
// Transformation with validation
27
z.string()
28
.min(1)
29
.transform((val) => val.trim())
30
.transform((val) => val.toUpperCase())
31
32
// Object transformation
33
z.object({
34
firstName: z.string(),
35
lastName: z.string(),
36
}).transform((data) => ({
37
fullName: `${data.firstName} ${data.lastName}`,
38
}))
39
```
40
41
## Pipe
42
43
Chain schemas together: validate with first schema, then second.
44
45
```typescript { .api }
46
function pipe<A extends ZodTypeAny, B extends ZodTypeAny>(a: A, b: B): ZodPipeline<A, B>;
47
```
48
49
**Examples:**
50
```typescript
51
// String to number pipeline
52
z.pipe(z.string(), z.number())
53
// Parses "42" as string, then coerces to number
54
55
// Custom pipeline
56
const StringToDatePipeline = z.pipe(
57
z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
58
z.string().transform((s) => new Date(s))
59
)
60
61
// Multi-stage pipeline
62
z.pipe(
63
z.string(),
64
z.string().transform((s) => s.trim()),
65
z.string().transform((s) => s.toLowerCase()),
66
z.string().email()
67
)
68
```
69
70
## Codec
71
72
Bidirectional encoding/decoding with custom logic.
73
74
```typescript { .api }
75
function codec<A extends ZodTypeAny, B extends ZodTypeAny = ZodTypeAny>(
76
in_: A,
77
out: B,
78
params: {
79
decode: (value: z.output<A>, payload: ParsePayload<z.output<A>>) => z.input<B> | Promise<z.input<B>>;
80
encode: (value: z.input<B>, payload: ParsePayload<z.input<B>>) => z.output<A> | Promise<z.output<A>>;
81
}
82
): ZodCodec<A, B>;
83
```
84
85
**Examples:**
86
```typescript
87
// Date codec
88
const DateCodec = z.codec(
89
z.string().transform((s) => new Date(s)),
90
z.date().transform((d) => d.toISOString())
91
);
92
93
DateCodec.decode("2024-01-01"); // Date object
94
DateCodec.encode(new Date()); // ISO string
95
96
// Custom codec
97
const Base64Codec = z.codec(
98
z.string(),
99
z.string(),
100
{
101
decode: (str) => Buffer.from(str, "base64").toString("utf8"),
102
encode: (str) => Buffer.from(str, "utf8").toString("base64"),
103
}
104
);
105
```
106
107
## Preprocess
108
109
Transform input before validation.
110
111
```typescript { .api }
112
function preprocess<T extends ZodTypeAny>(
113
preprocessor: (arg: unknown) => unknown,
114
schema: T
115
): ZodEffects<T>;
116
```
117
118
**Examples:**
119
```typescript
120
// Trim before validation
121
z.preprocess((val) => String(val).trim(), z.string().min(1))
122
123
// Coerce to number
124
z.preprocess((val) => Number(val), z.number())
125
126
// Parse JSON
127
z.preprocess(
128
(val) => typeof val === "string" ? JSON.parse(val) : val,
129
z.object({ name: z.string() })
130
)
131
132
// Form data preprocessing
133
z.preprocess(
134
(val) => val === "" ? undefined : val,
135
z.string().optional()
136
)
137
```
138
139
## Common Patterns
140
141
```typescript
142
// Parse and format dates
143
const DateSchema = z.string().transform((s) => new Date(s));
144
const FormattedDateSchema = z.date().transform((d) => d.toISOString());
145
146
// Parse JSON strings
147
const JSONSchema = z.string().transform((s) => JSON.parse(s));
148
const ParsedJSONSchema = z.preprocess(
149
(val) => typeof val === "string" ? JSON.parse(val) : val,
150
z.object({ data: z.any() })
151
);
152
153
// Normalize input
154
const NormalizedEmailSchema = z
155
.string()
156
.transform((s) => s.trim().toLowerCase())
157
.email();
158
159
// Chain transformations
160
const ProcessedSchema = z
161
.string()
162
.transform((s) => s.trim())
163
.transform((s) => s.toLowerCase())
164
.transform((s) => s.replace(/\s+/g, "-"))
165
.refine((s) => s.length > 0);
166
167
// Form data transformation
168
const FormSchema = z.object({
169
name: z.preprocess((val) => String(val).trim(), z.string()),
170
age: z.preprocess((val) => Number(val), z.number()),
171
active: z.preprocess((val) => val === "true", z.boolean()),
172
});
173
174
// Pipeline for multi-step validation
175
const UsernamePipeline = z.pipe(
176
z.string(),
177
z.string().min(3).max(20),
178
z.string().regex(/^[a-z0-9_]+$/),
179
z.string().transform((s) => s.toLowerCase())
180
);
181
```
182
183
## Type Inference
184
185
```typescript
186
const TransformSchema = z.string().transform((s) => s.length);
187
188
type Input = z.input<typeof TransformSchema>; // string
189
type Output = z.output<typeof TransformSchema>; // number
190
191
const PipelineSchema = z.pipe(z.string(), z.number());
192
type In = z.input<typeof PipelineSchema>; // string
193
type Out = z.output<typeof PipelineSchema>; // number
194
```
195