I've been relying on JSON→Zod generators to scaffold schemas from API responses,
and kept wondering — how much do they actually infer vs just dumping z.string()?
So I ran three tools on the same payload to find out.
Input (same for all):
{ "status": "active", "email": "a@b.com", "id": "a1b2c3d4-…",
"joined": "2026-01-01T00:00:00Z", "age": 30, "website": "https://a.com" }
json-to-zod
z.object({
status: z.string(), email: z.string(), id: z.string(),
joined: z.string(), age: z.number(), website: z.string(),
})
Zero format detection. Technically correct, validates nothing.
quicktype (-l typescript-zod)
z.object({
status: z.string(), email: z.string(), id: z.string(),
joined: z.coerce.date(), // caught the date
age: z.number(), website: z.string(),
})
1 out of 6. Better, but stops there.
TypeMorph (disclosure: side project I built — take with salt)
z.object({
status: z.enum(["active", "pending", "closed"]),
email: z.email(),
id: z.uuid(),
joined: z.iso.datetime(),
age: z.number().int().min(0).max(150),
website: z.url(),
})
The interesting design questions this raised for me:
-
Enums from a single sample → you get
z.enum(["active"])which rejects valid future values. You need multiple samples aggregated before enums are safe. Worth it? -
Name-based heuristics (
age → .int().min(0).max(150)) — useful shortcut or too opinionated? -
Open-vocab fields like
currencyorcountryshould stayz.string()even if they look like enums
Is anyone doing something smarter here? Curious what approach others take
when generating schemas from real API data.
Top comments (0)