Capítulo 36 de 61
Every Zod schema technically supports forward (.parse()/.decode()) and backward (.encode()) processing, but the distinction only matters when input and output types diverge — which is exactly what z.codec() is built for, and it composes like any other schema (nestable in objects/arrays/pipes).
.parse()/.decode() go input → output; .encode() goes output → input. For most schemas input and output types are identical, so the distinction is invisible; for codecs they genuinely differ.z.invertCodec(codec): swaps input/output schemas and the decode/encode functions — but only for the codec passed in, not recursively for codecs nested deeper inside it.z.object(), arrays, pipes, with no special restrictions. objectSchema.decode({...}) decodes every nested codec field in one call..decode()/.encode() vs. unknown-typed .parse(): .parse() accepts unknown (runtime-checked); .decode()/.encode() have strongly-typed parameters matching the codec's input/output type respectively, so passing the wrong type is a compile error, not just a runtime one.decode/encode functions; .decodeAsync()/.encodeAsync() return promises, and .safeDecode()/.safeDecodeAsync() (plus the encode equivalents) return a result object instead of throwing.const stringToDate = z.codec(z.iso.datetime(), z.date(), {
decode: (isoString) => new Date(isoString),
encode: (date) => date.toISOString(),
});
// nested inside an object — decodes the whole payload in one call
const payloadSchema = z.object({ startDate: stringToDate });
payloadSchema.decode({ startDate: "2024-01-15T10:30:00.000Z" });
// => { startDate: Date }
// type-safe inputs: parse() accepts unknown, decode()/encode() don't
stringToDate.parse(12345); // no compile error (fails at runtime)
stringToDate.decode(12345); // compile error — expects a string
// async + safe variants
stringToDate.decodeAsync("2024-01-15T10:30:00.000Z"); // Promise<Date>
stringToDate.safeDecode("2024-01-15T10:30:00.000Z");
// => { success: true, data: Date } | { success: false, error: ZodError }
.decode()/.encode() versus .parse().z.invertCodec() inverts codecs nested inside the one you pass it: it doesn't — invert nested codecs individually where you build the containing schema..decode()/.encode() exist specifically because .parse()'s unknown input type can't catch obviously-wrong-typed calls at compile time — use them when the input is already typed in your application code..safeDecode(), .decodeAsync(), etc.) the same way you would with .safeParse()/.parseAsync().z.codec() API these mechanics build on.z.input()/z.output() for reaching codecs nested deep inside a schema tree.