Which side of a schema a document site is describing.
A zod schema has two shapes, and for anything carrying a .default(), a
.transform(), a .coerce, or the ordinary key-stripping of z.object,
they differ. 'input' is what a client may SEND; 'output' is what the
server holds after parsing. A request body, a query string, a path parameter
and a header are all 'input'; a response body is 'output'.
Getting this wrong is not cosmetic — it makes a document contradict the server it describes. Measured against zod 4.4 on the three object modes:
| schema | unknown key at runtime | 'output' says |
'input' says |
|---|---|---|---|
z.object |
accepted, stripped | additionalProperties: false |
(absent) |
z.strictObject |
rejected | additionalProperties: false |
false |
z.looseObject |
accepted, kept | additionalProperties: {} |
{} |
— so on a REQUEST body the output view documents a restriction the server
does not apply, while a strict schema keeps its false under either view. A
.default() field is likewise required in the output view (the parsed
object always has it) and optional in the input view (the client may omit
it, which is what a default is FOR).
Only the zod v4 path reads this. The v3 path hand-walks _def and has no
io concept; what it produces is already the INPUT view — it marks a
.default() field optional and documents a .transform() by its source
type — so v3 request bodies were always right and v3 responses carry the
same input-shaped view they always have. Closing that is a v3-transformer
change with no consumer asking for it, and is deliberately not done here.