Stop Writing Zod Schemas by Hand — Generate Them from Real JSON
Table of Contents
You can generate Zod schemas automatically from real JSON instead of writing them by hand. Paste any JSON structure into the free converter and get a complete Zod schema in seconds — no signup, no copy-paste from documentation, no risk of mistyping a field name.
This page explains why hand-writing schemas is a time sink, what the generator produces, and how to tune the output for production.
The Case Against Hand-Writing Zod Schemas
Consider writing a Zod schema for a Stripe PaymentIntent response by hand. The object has 40+ fields, nested objects, union types, and nullable strings. You would need to:
- Read the Stripe API docs for every field
- Decide if each field is optional or required
- Map each JSON type to the correct Zod primitive
- Handle nested objects recursively
- Not make a single typo in a field name
That might take 30-60 minutes for a complex response. The generator takes 5 seconds. And the generator cannot make a typo — it reads the actual field names from real data.
What the Generator Produces
Paste this into the free JSON to Zod converter:
{
"orderId": "ORD-2024-8821",
"status": "shipped",
"customer": {
"id": 1042,
"email": "[email protected]"
},
"items": [
{ "sku": "TSHIRT-L-BLK", "qty": 2, "unitPrice": 29.99 }
],
"discount": null,
"shippedAt": "2024-11-03T14:30:00Z"
}
Output:
const schema = z.object({
orderId: z.string(),
status: z.string(),
customer: z.object({
id: z.number(),
email: z.string()
}),
items: z.array(z.object({
sku: z.string(),
qty: z.number(),
unitPrice: z.number()
})),
discount: z.null(),
shippedAt: z.string()
});
Accurate field names, correct types, nested structure — done in one paste.
Sell Custom Apparel — We Handle Printing & Free ShippingFive Refinements to Make After Generation
The generator handles structure. You handle semantics — five typical refinements:
- String formats:
email: z.string().email(),shippedAt: z.string().datetime() - Nullable fields:
discount: z.number().nullable()(if it can be a number OR null) - Optional fields: Add
.optional()to fields that may be absent in some responses - Enums:
status: z.enum(["pending", "shipped", "delivered", "cancelled"]) - Number constraints:
qty: z.number().int().positive(),unitPrice: z.number().min(0)
These five refinements cover 90% of what you would add anyway. The generator saves the structural work, which is the most tedious part.
Use the .ts Download for Clean Project Structure
The converter includes a Download .ts button. Use it to get a clean file you can drop directly into your project:
- Download the
.tsfile - Move it to
src/schemas/order.ts(or your schema directory) - Add
import { z } from 'zod';at the top (the downloaded file does not include the import) - Export the schema and the inferred type:
export type Order = z.infer<typeof schema>; - Add your refinements
End result: a production-ready schema file in under two minutes. No copy-paste from documentation, no guessing at field types.
Try It Free — No Signup Required
Runs 100% in your browser. No data is collected, stored, or sent anywhere.
Open Free JSON to Zod ConverterFrequently Asked Questions
Can I generate Zod schemas from real API responses automatically?
Yes. Paste any valid JSON from an API response into the free tool at /developer-tools/json-to-zod/. The schema generates instantly from the actual data — correct field names, correct types, nested objects handled.
Is it safe to paste API responses into an online tool?
Yes. The converter runs 100% in your browser. Your JSON is never sent to any server. No data is stored, logged, or transmitted anywhere. You can verify this by opening browser DevTools and checking the Network tab — no outgoing requests are made.
Does the generated Zod schema include the import statement?
No. The output starts with "const schema = z.object(...)". Add "import { z } from 'zod';" at the top of your file. Also rename the variable from "schema" to something descriptive like "OrderSchema".
What if my JSON has a field that can be a string or a number?
The generator maps each field to the type of the value in your sample. If a field is sometimes a string and sometimes a number, the sample will only show one. Change the output to z.union([z.string(), z.number()]) for those fields.

