JSON Schema to Code in 2026: quicktype vs json-schema-to-typescript vs datamodel-codegen
Every team eventually writes the same file twice: once as a JSON Schema, and once as hand-typed models, which then drift apart until production accepts a payload the schema rejects. The fix is a generator — but the three most popular ones are not interchangeable. One emits 19 languages and infers schemas from samples, one emits TypeScript and nothing else, and one emits validated Python classes with $ref resolution that actually survives a real OpenAPI document.
Below is the comparison I wish I had before wiring one of these into CI, including the exact commands, the actual output shapes, and where each one silently fails.
TL;DR: The 60-Second Verdict
- Polyglot repo (TypeScript + Go + Swift + Rust + Java in one tree): quicktype. v26.0.0, Apache-2.0, 13,910 stars, and it is the only tool here that can infer a schema from sample JSON and then emit models in 19 target languages from that same schema.
- TypeScript-only, schema already committed, want a one-line build step: json-schema-to-typescript (
json2ts). 3,353 stars, MIT, 1 dependency-light CLI, glob support, and it does not try to be a platform. - Python backend with Pydantic v2 models, OpenAPI/Avro/Protobuf mixed in: datamodel-code-generator. 4,056 stars, MIT, v0.92.0, and the only one of the three that handles
allOf/oneOf/anyOfplus multi-format inputs without a wrapper script.
If you only read one line: quicktype for breadth, json2ts for TypeScript discipline, datamodel-codegen for Python correctness.
The Three at a Glance
GitHub numbers pulled 2026-11-10.
| quicktype | json-schema-to-typescript | datamodel-code-generator | |
|---|---|---|---|
| Repo | quicktype/quicktype | bcherny/json-schema-to-typescript | koxudaxi/datamodel-code-generator |
| Stars | 13,910 | 3,353 | 4,056 |
| Last push | 2026-10-30 | 2026-09-07 | 2026-11-10 |
| Latest | v26.0.0 | npm-only (no GitHub releases) | 0.92.0 |
| License | Apache-2.0 | MIT | MIT |
| Language | TypeScript (CLI) | TypeScript | Python |
| Output languages | 19 (TS, Go, Rust, Swift, Java, Kotlin, C#, Dart, Ruby, Python, C++, Scala, PHP, Elm, Haskell, Objective-C, Flow, Pike, JSON Schema) | TypeScript only | Python only (Pydantic v2, dataclass, TypedDict, msgspec) |
| Input formats | JSON, JSON Schema, TypeScript, GraphQL | JSON Schema | OpenAPI 3, AsyncAPI, JSON Schema, Avro, XSD, Protobuf/gRPC, GraphQL, raw JSON/YAML/CSV |
| Infers schema from samples | yes | no | no |
| $ref / composition | good | good for local refs | strongest (allOf, oneOf, anyOf, remote refs) |
| Web playground | app.quicktype.io | live demo | docs playground (local, Pyodide) |
Decision Matrix: Pick by Situation
| Your situation | Pick | Reason |
|---|---|---|
| Monorepo with TS frontend and Go/Rust services sharing contracts | quicktype | one schema, many --lang targets, byte-identical naming rules |
Only TypeScript, schema lives in schemas/*.json | json2ts | json2ts -i 'schemas/**/*.json' -o types/ and you are finished |
| Python + FastAPI + Pydantic v2 | datamodel-codegen | generates validated models, not just type hints |
| You have sample payloads but no schema yet | quicktype | -l schema infers a reviewable schema first |
| You need Avro or Protobuf schemas as input | datamodel-codegen | explicit multi-format support |
| You want zero new runtime dependencies | json2ts | output is pure .d.ts, no runtime library |
| You need JSON Schema output as an artifact | quicktype | it is one of the target languages |
quicktype — Inference First, Then Nineteen Languages
quicktype’s recommended workflow is not “schema in, code out”. It is sample in, schema out, review, then code out — which is the correct order for legacy APIs where nobody documented the contract:
| |
Install is a single global npm package:
| |
And the fast path for one-off conversions still works exactly as documented:
| |
The -s schema flag is the part people miss. Without it, quicktype infers from your input and you get a different result than the schema your backend validates against. Always generate from the schema, not from a sample, once a schema exists.

Where it breaks: Type naming is inferred from property names and can produce awkward identifiers in languages with strict conventions. Output is idiomatic but opinionated — you will likely wrap it in a formatter (gofmt, rustfmt, prettier) as a post-step. And because it supports 19 targets, edge cases in exotic languages get fixed slower than TypeScript ones.
json-schema-to-typescript — The Boring, Correct TypeScript Answer
json2ts does one thing. Given this schema:
| |
it produces exactly what you would have typed by hand:
| |
Note the details that make it trustworthy: required becomes optionality, the enum becomes a string literal union rather than string, and the description survives as a JSDoc comment. Those three behaviors are why teams keep it in a prebuild script for years.
CLI usage, straight from the README:
| |
That glob form is the whole build integration for most projects — one line in package.json:
| |
Where it breaks: it is TypeScript-only, by design. There are no GitHub releases (npm versions are the source of truth), so pin the exact npm version in package.json rather than a range. And it does not infer schemas — if all you have is sample payloads, you need quicktype or a hand-written schema first.
datamodel-code-generator — Python Models That Actually Validate
For Python the question is not “types” but “validation”. A TypedDict gives you editor completion; a Pydantic model rejects a payload at the boundary. datamodel-codegen targets the latter, and it is the only tool here that consumes OpenAPI, AsyncAPI, Avro, XSD, Protobuf and GraphQL as inputs.
Install it as an isolated CLI tool rather than a project dependency:
| |
Then generate from a JSON Schema:
| |
The --preset flag is a 2026-era addition: presets bundle a coherent set of options for a target Python version, so you stop hand-maintaining twenty flags. --output-model-type lets you pick pydantic_v2.BaseModel, pydantic_v2.dataclass, dataclasses.dataclass, typing.TypedDict, or msgspec.Struct from the same schema.
The generated header is explicit about provenance, which matters when a reviewer asks why a file has no owner:
| |
Where it breaks: it is Python-only, and the flag surface is large enough that two teams will produce different output from the same schema unless the invocation is committed to the repo. It also regenerates by overwriting files, so directories under generation should never be hand-edited — treat them as build artifacts.
The Pipeline Pattern That Actually Holds Up
After running all three, the pattern that survives contact with real teams is: schema is the source of truth, generation is a build step, generated files are committed or discarded consistently, and CI fails when regeneration produces a diff.
| |
That single check catches the most expensive class of bug in schema-driven codebases: a schema PR merged without regenerating the models, so the runtime validates one shape and the type system believes another.
If your schemas are OpenAPI documents rather than standalone JSON Schema, our OpenAPI generator comparison covers the server-stub side of the same problem. And if you are choosing a validator for the generated models at runtime, JSON Schema validation with ajv, prism and joi is the companion read. On the JVM specifically, see Java JSON Schema validators.
Pitfalls That Cost Real Debugging Time
1. Generating from samples instead of schemas. quicktype will happily infer a schema from one payload and mark every field required. Commit the inferred schema, edit it, then generate from the schema with -s schema.
2. Version pinning drift. quicktype is at v26.0.0, datamodel-codegen at 0.92.0, and json2ts publishes only to npm. Pin exact versions; generated code changing because a minor release changed naming rules is a genuinely miserable diff to review.
3. Committing generated files without a drift check. Either generate in CI and never commit, or commit and diff. Doing neither means the two copies diverge within a sprint.
4. Ignoring additionalProperties. json2ts defaults to closed objects only when the schema says so; a schema with additionalProperties: true yields an index signature, which quietly weakens every downstream type.
5. Forgetting that $ref resolution differs. Remote refs work in some tools and not others. Vendor your schemas locally before generating, or your build breaks the day the upstream URL changes.
6. Using enum values as bare strings. All three tools can emit literal unions or Python Enum classes. Prefer them — a string field with an enum constraint is a runtime failure waiting to be shipped.
FAQ
Which JSON Schema code generator is fastest to set up?
json-schema-to-typescript. One npx json2ts -i schema.json -o types.d.ts and you have output, with no configuration file and no runtime dependency. quicktype needs a global install and a decision about --lang; datamodel-codegen needs --input-file-type, --output-model-type, and ideally a --preset.
Can quicktype generate Python models from JSON Schema?
Yes. quicktype -s schema schema.json -l python -o models.py. It produces typed classes, but it does not produce Pydantic validators. If you need boundary validation rather than type hints, datamodel-code-generator’s pydantic_v2.BaseModel output is the correct choice.
Does json-schema-to-typescript support $ref and definitions?
Yes, for local references. It resolves definitions/$defs and emits shared interfaces. It is also the tool of the three with the narrowest scope, so what it supports it tends to support correctly, including allOf composition and oneOf unions.
How do I keep generated models in sync with the schema?
Run generation in CI and diff the result against the committed output. If the diff is non-empty, fail the build with a message telling the author to regenerate. This is three lines of shell and it eliminates the entire class of stale-contract bugs.
Is datamodel-code-generator only for OpenAPI?
No. It accepts OpenAPI 3, AsyncAPI, JSON Schema, Avro, XML Schema, Protobuf/gRPC, GraphQL and raw JSON/YAML/CSV, and can also retarget existing Python classes with --input-model path/to/file.py:ClassName. That breadth is why it is the default in Python codebases that consume more than one schema dialect.
Do these tools work offline?
Yes, all three are local CLI tools with no required network access, and quicktype’s web app is documented to work offline without sending sample data anywhere. Only remote $ref resolution requires network access, which is another reason to vendor schemas into the repo.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com