Capítulo 45 de 61
z.toJSONSchema() converts a Zod schema into a standard JSON Schema document (useful for OpenAPI specs and AI structured-output definitions); the experimental z.fromJSONSchema() goes the other direction.
z.toJSONSchema(schema, params?): converts to JSON Schema, defaulting to Draft 2020-12. Every check/type is mapped to its closest JSON Schema equivalent.z.fromJSONSchema(jsonSchema): experimental, converts a JSON Schema object into a Zod schema — not considered stable API yet.bigint, int64, symbol, undefined, void, date, map, set, transform, nan, and custom have no JSON Schema equivalent. Default behavior is to throw; unrepresentable: "any" converts them to {} (JSON Schema's "anything"); a function lets you decide per-schema, returning a replacement JSON Schema, "any", or "throw" — it receives zodSchema, path, and message for building a precise error or substitution.io: "input" | "output" (default "output"): schemas whose input/output diverge (pipes, .default(), coerced primitives) — io: "input" extracts the pre-transform shape instead of the post-transform one.target: JSON Schema dialect — "draft-2020-12" (default), "draft-07", "draft-04", or "openapi-3.0".metadata: .meta({...}) (a shortcut for registering in z.globalRegistry) copies arbitrary fields straight into the output JSON Schema, and metadata overrides whatever keyword Zod would otherwise generate — including things like type itself.const schema = z.object({ name: z.string(), age: z.number() });
z.toJSONSchema(schema);
// => { type: "object", properties: { name: {type:"string"}, age: {type:"number"} }, required: ["name","age"], additionalProperties: false }
// unrepresentable types
z.toJSONSchema(z.bigint()); // throws by default
z.toJSONSchema(z.bigint(), { unrepresentable: "any" }); // => {}
// io: input vs output side of a transforming pipe
const piped = z.string().transform(val => val.length).pipe(z.number());
z.toJSONSchema(piped); // => { type: "number" } (output)
z.toJSONSchema(piped, { io: "input" }); // => { type: "string" } (input)
// metadata copies through, and overrides generated keywords
const emailSchema = z.string().meta({ title: "Email address", description: "Your email address" });
z.toJSONSchema(emailSchema);
// => { type: "string", title: "Email address", description: "Your email address" }
z.toJSONSchema(z.string().meta({ type: "number" }));
// => { type: "number" } — metadata wins over the string type Zod would normally emit
bigint, date, Map/Set, and custom schemas need explicit handling (unrepresentable option) — don't assume z.toJSONSchema() "just works" on an arbitrary schema without checking for these.unrepresentable is the escape hatch for the handful of Zod types JSON Schema fundamentally can't express — decide up front whether to throw, degrade to {}, or supply a manual replacement..meta() fields aren't just documentation — they can override the generated JSON Schema output entirely, which is powerful but means a stray .meta({ type: ... }) can silently change the emitted schema.io: "input" whenever the JSON Schema needs to describe what callers send, not what the schema ultimately produces after transforms..meta()/z.globalRegistry, the mechanism this chapter's metadata handling builds on.override option.