I get it. Zod is nice. The API is clean, the TypeScript integration is smooth, and everyone in the React ecosystem uses it. But we need to talk about the elephant in the room: Zod only works in JavaScript/TypeScript. JSON Schema works everywhere.
This isn’t a hot take. It’s just reality.
The Problem With Language-Specific Validation
You build a REST API in TypeScript. You use Zod for validation. Great. Then:
- Your Python microservice needs to validate the same data structures
- Your Go service needs to consume the same API
- Your mobile team needs to validate forms client-side
- Your Rust CLI tool needs to work with the same data
- Your documentation is out of sync because you’re maintaining schemas in multiple places
Now you’re either:
- Rewriting the same validation logic in 4+ languages
- Trusting that everyone interprets the requirements the same way
- Accepting inconsistent validation across your stack
This is dumb. We solved this problem decades ago with JSON Schema.
What JSON Schema Actually Gives You
JSON Schema is a specification, not a library. It’s a standard way to describe JSON data structures. Once you define a schema, you can:
Validate in any language: Libraries exist for JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, etc. Same schema, consistent validation everywhere.
Generate documentation: Tools like Redoc, Swagger UI, and Stoplight can generate beautiful API docs directly from your schemas. No manual doc writing.
Generate code: Generate types for TypeScript, Go structs, Python dataclasses, etc. from the same schema. Single source of truth.
Generate forms: Tools like react-jsonschema-form can create entire UIs from schemas. Define the schema once, get forms for free.
Generate tests: Property-based testing tools can generate test cases from schemas automatically.
Migrate data: Schema versioning lets you handle migrations systematically instead of hoping for the best.
The Real Advantage: Language Agnostic
This is the big one. Your schema lives independently of your codebase. It’s just a JSON document.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email"
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
}
},
"required": ["email"]
}
Now use it:
TypeScript:
import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(schema);
validate(data); // true/false
Python:
import jsonschema
jsonschema.validate(instance=data, schema=schema)
Go:
import "github.com/xeipuuv/gojsonschema"
result, _ := gojsonschema.Validate(schemaLoader, dataLoader)
Rust:
use jsonschema::JSONSchema;
let compiled = JSONSchema::compile(&schema)?;
compiled.validate(&data)?;
Same schema. Four languages. Consistent validation. This is how software should work.
Why Zod Became Popular (And Why That’s Fine)
Zod took off because:
- TypeScript’s type system doesn’t do runtime validation
- The API is genuinely nice to use
- Type inference is magical
- It’s designed specifically for TypeScript’s needs
These are real advantages. If you’re building a pure TypeScript/JavaScript project with no plans to expand beyond that, Zod is fine. Use it.
But most projects don’t stay in one language forever. That TypeScript API becomes a Python service. That Node backend needs a Go sidecar. That web app needs a Rust CLI companion.
Now your Zod schemas are technical debt.
JSON Schema Isn’t Perfect
Let’s be honest about the downsides:
Verbose: JSON Schema definitions are longer than equivalent Zod code. This is the price of being language-agnostic.
No type inference: In TypeScript, you define the schema and separately define the type. Tools exist to bridge this, but it’s not as seamless as Zod.
Learning curve: The spec is comprehensive, which means there’s more to learn. Zod is simpler because it does less.
Tooling fragmentation: Different validators have slightly different features. The spec has multiple draft versions. It’s messier than a single library.
Validation performance: Some JSON Schema validators are slower than hand-written code or Zod. Usually doesn’t matter, but it can.
When To Actually Use JSON Schema
Multi-language projects: If you have services in different languages, JSON Schema is non-negotiable. You need consistent validation.
Public APIs: Your API consumers might be using any language. Provide schemas they can use.
Documentation-first development: Generate docs, types, and validators from a single source.
Long-lived projects: As projects age, they tend to span more languages and platforms. Starting with JSON Schema saves refactoring later.
Configuration validation: Config files are language-agnostic. Schema should be too.
When To Use Zod
Pure TypeScript projects: If you’re confident you’ll never leave the TS ecosystem, Zod’s developer experience is better.
Rapid prototyping: For MVPs and experiments, Zod is faster to write.
Complex TypeScript-specific validation: If you need deep integration with TS types and advanced transformations, Zod excels here.
Hybrid Approach (The Pragmatic Option)
Here’s what I actually do:
- Define schemas in JSON Schema for core data models
- Store them in a
schemas/directory or separate repo - Generate TypeScript types from schemas using tools like
json-schema-to-typescript - Use Ajv for runtime validation in TypeScript
- Use the same schemas in other languages as needed
This gives you:
- Language-agnostic validation
- Single source of truth
- Type safety in TypeScript
- Easy integration with other languages later
Yes, it’s more upfront work than npm install zod. But it scales better.
The Bigger Picture
Zod is a symptom of JavaScript/TypeScript ecosystem insularity. We keep reinventing wheels that already exist as language-agnostic standards.
- We have JSON Schema, but we made Zod, Yup, Joi, Superstruct, etc.
- We have OpenAPI, but every framework has its own way to define routes
- We have JSON-RPC and gRPC, but we keep making bespoke RPC systems
This isn’t a JS-specific problem. Every ecosystem does this. But it’s worth stepping back and asking: does this need to be language-specific?
For validation, the answer is no. JSON Schema exists. It works. It’s been standardized since 2009. Use it.
Closing Thoughts
Zod isn’t bad. It’s a well-designed library that solves a real problem in TypeScript. But it’s a local maximum. JSON Schema is the global solution.
If you’re starting a new project today:
- Planning to stay 100% TypeScript forever? Zod is fine.
- Might use other languages? JSON Schema from day one.
- Building a public API? JSON Schema, no question.
- Need documentation generation? JSON Schema.
- Want to future-proof? JSON Schema.
The initial investment in JSON Schema pays off the moment you need to integrate with another language or tool. And you will. Eventually, everyone does.
Resources:
- JSON Schema Official Site
- Ajv - Fast JSON Schema validator for JS/TS
- json-schema-to-typescript - Generate TS types
- JSON Schema Store - Pre-made schemas for common formats
Choose the right tool for the problem. For validation that needs to work everywhere, that tool is JSON Schema.