Zod Schema Generator from JSON

Paste any JSON object and get a TypeScript Zod schema instantly.

Infers a Zod v3 schema from a JSON sample, correctly handling nested objects, arrays, optional and nullable fields, and primitive types. Outputs a ready-to-paste TypeScript snippet with an inferred type alias. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

How does it decide if a field is optional?

From a single object it cannot know optionality, so every present key is treated as required by default. If you paste an array of objects, fields missing from some items are inferred as .optional(), and explicit null values become .nullable().

Zod schema generator from JSON

Validating external data — API responses, webhook payloads, config files — with Zod is one of the cleanest patterns in modern TypeScript, but hand-writing the schema for a deeply nested object is tedious and error-prone. This tool takes a real JSON sample and infers a matching Zod v3 schema, including nested objects, arrays, nulls, and a generated TypeScript type alias.

The typical workflow without this tool: copy the API response, stare at a deeply nested structure, type out every z.object() by hand, miss a field, ship broken validation. This generator short-circuits that cycle to a few seconds.

How it works

The generator recursively walks your parsed JSON. The mapping is straightforward:

JSON valueZod output
"hello"z.string()
42 or 3.14z.number()
true / falsez.boolean()
null.nullable() modifier added
{ ... }z.object({ ... }) (recursed)
[...]z.array(elementSchema)
[] (empty)z.array(z.unknown())

When you supply an array of objects at the top level, the tool unions keys across all items: a key missing from some items becomes .optional(), and any field that appears as null in at least one item becomes .nullable(). The final output is a named schema constant plus a z.infer<typeof Schema> type alias you can use directly.

Worked example

Paste this GitHub-style user payload:

{
  "id": 12345,
  "login": "alice",
  "name": "Alice Smith",
  "email": null,
  "bio": "TypeScript enthusiast",
  "public_repos": 42,
  "site_admin": false
}

The tool produces something like:

import { z } from "zod";

export const Schema = z.object({
  id: z.number(),
  login: z.string(),
  name: z.string(),
  email: z.string().nullable(),
  bio: z.string(),
  public_repos: z.number(),
  site_admin: z.boolean(),
});

export type Schema = z.infer<typeof Schema>;

From here you can call Schema.parse(response) to throw on invalid data, or Schema.safeParse(response) to get a typed result/error union without throwing.

Adding refinements after generation

The generated schema is a structural skeleton, not the final production schema. Once the shape looks right, layer on value-level constraints:

  • z.string().email() — validate email format
  • z.string().uuid() — enforce UUID format on ID strings
  • z.number().int().min(0) — non-negative integers
  • z.string().url() — URL strings
  • z.array(z.string()).min(1) — non-empty arrays
  • .transform(val => new Date(val)) — parse ISO date strings to Date

These additions are mechanical but often catch real bugs at the boundary between your code and an external service.

Common mistakes and edge cases

Single object versus array of objects. If you paste just one object, every field appears as required even if the API sometimes omits it. Paste a few representative payloads — or a JSON array of multiple responses — so the tool can detect which keys genuinely vary.

Discriminated unions. If your API returns different shapes based on a type field (a common REST pattern), the generator will merge the shapes into one z.object(). After generation, split it into a z.discriminatedUnion("type", [...]) manually — that gives Zod the information to give tighter error messages.

Dates as strings. JSON has no native date type, so "2024-01-15T08:30:00Z" comes out as z.string(). Add .datetime() or .transform(s => new Date(s)) once you know the field is a date.

Empty arrays. A [] in your sample becomes z.array(z.unknown()) because the element type is unknowable. Provide a sample with at least one element to get a real element schema.

Why Zod over TypeScript interfaces alone

TypeScript types are erased at runtime. A fetch() response typed as an interface gives you zero runtime protection — any malformed field passes straight through. Zod validates and coerces at the boundary, so your application code can trust that user.id is actually a number, not whatever the server happened to return. The z.infer alias means you write the schema once and get both the runtime check and the static type for free.

Tips and notes

  • Give a full sample. Optionality is inferred from what is present. The more complete and representative your JSON, the more accurate the schema.
  • Paste arrays for better optionality. An array of several objects lets the tool detect which fields are sometimes absent or null.
  • Refine afterwards. Add .email(), .min(), .uuid(), and other refinements once the structure is right — the tool gives you the skeleton.
  • Local only. Your payload never leaves the browser — safe to paste real API responses, tokens, or production data.