Three tools, one job, different worlds
"Validate this data against a shape" is one of the most common tasks in software, and three tools dominate: JSON Schema (language-neutral, declarative), Zod (TypeScript-first, code-as-schema), and Pydantic (Python-first, class-as-schema). They overlap enough to feel interchangeable and differ enough that picking wrong creates duplicate sources of truth. Here's how to choose — and how to convert between them so you don't maintain three.
JSON Schema: portable and declarative
JSON Schema *is* JSON, which is its superpower: it's the one format every language, API tool, and database can read. OpenAPI, config validators, form generators, and LLM structured-output modes all speak it.
{
"type": "object",
"properties": { "email": { "type": "string", "format": "email" }, "age": { "type": "integer", "minimum": 0 } },
"required": ["email"]
}Strengths: universal, tool-agnostic, the lingua franca for cross-system contracts and LLM schemas. Weakness: it's data, not code — no type inference, and verbose to write by hand.
Zod: schema and TypeScript type in one
Zod defines schemas as TypeScript code and infers the static type from them, so validation and types never drift:
import { z } from "zod";
const User = z.object({
email: z.string().email(),
age: z.number().int().min(0).optional(),
});
type User = z.infer<typeof User>; // { email: string; age?: number }Strengths: best-in-class DX in TypeScript, one source of truth for runtime checks and compile-time types, powers the Vercel AI SDK. Weakness: it's TypeScript-only — a Zod schema can't be handed to a Python service or a generic API tool.
Pydantic: Python's class-based validator
Pydantic does for Python what Zod does for TypeScript — the model *is* the schema, with editor autocompletion and runtime validation:
from pydantic import BaseModel, EmailStr, Field
class User(BaseModel):
email: EmailStr
age: int | None = Field(default=None, ge=0)Strengths: ubiquitous in Python, especially AI tooling (Pydantic AI, Instructor, FastAPI). Weakness: Python-only, like Zod is TS-only.
How to choose
- Crossing language or system boundaries (APIs, configs, LLM structured output, storing the schema itself)? JSON Schema.
- Building a TypeScript app or LLM pipeline? Zod.
- Building in Python? Pydantic.
The key insight: JSON Schema is the interchange format; Zod and Pydantic are the ergonomic front-ends. Both can *emit* JSON Schema (z.toJSONSchema(), model_json_schema()) and be *generated from* it — so you keep one canonical schema and derive the others.
Keep one source of truth
- Treat a JSON Schema as canonical for anything shared across languages or sent to an LLM.
- Generate the code layer: convert JSON to a Zod schema for TS, or to Pydantic/Python for Python.
- Validate real data against the canonical schema in CI so all three stay honest.
For LLM-specific angles see Zod for AI and Validate LLM JSON Against a Schema.