Skip to content
{ }JSON Formatter

JSON to Zod Schema

Generate a Zod schema from a JSON sample — nested z.object(), z.array(), and optional()/nullable() inferred from your data. Get runtime validation and an inferred TypeScript type in one.

Input
Output
Paste input and press Generate Zod.

Runtime validation from a sample

Zod gives you a schema that both validates data at runtime and produces a TypeScript type — so an API response you can't fully trust becomes typed and checked at the boundary. Paste a representative JSON sample and this tool infers the schema: scalars map to z.string(), z.number(), z.boolean(); nested objects become inline z.object({ … }); arrays become z.array(…). It's emitted as one inline tree, so there are no ordering pitfalls — paste and run.

Optional & nullable, inferred

A field is marked .optional() when it is missing from some records, and .nullable() when a null appears alongside a real value — the two Zod handles separately, and so does this tool. Integers get z.number().int(); genuinely mixed or always-null values fall back to z.any() / z.null(). The generated z.infer<typeof Schema> gives you the matching TypeScript type for free.

Finish & use it

Rename the exported const to suit your domain, tighten scalars where you know more (.email(), .min(), .uuid()), and you have a validator. Prefer plain types? JSON to TypeScript emits interfaces; for a GraphQL schema use JSON to GraphQL.

Frequently asked questions

How are optional and nullable decided?

A field becomes .optional() when it is absent from at least one record in the sample, and .nullable() when a null value appears alongside a non-null one. If a field is only ever null, it is z.null(); if it holds genuinely mixed types, it falls back to z.any().

Does it produce a TypeScript type too?

Yes. Alongside the schema it exports type Schema = z.infer<typeof Schema>, so you get the runtime validator and the static type from one definition — the core benefit of Zod.

Why is the schema inline rather than separate named schemas?

Nested objects are emitted as inline z.object({…}) inside their parent. That keeps everything in one const and avoids the declaration-order pitfalls you would hit if child schemas were referenced before they were defined.

Can I refine the generated schema?

Absolutely — treat it as a starting point. Add .email(), .min(1), .uuid(), .default(...) and custom .refine() checks where you know the real constraints. The structure and base types are the tedious part done for you.