← Essays
§ Essay

Why we do not re-infer from JSON Schema

quicktype — the engine behind this site — can take JSON Schema as input instead of JSON samples. You paste a schema, it emits types in whatever language. It is a legitimate feature and it works well for the case it is built for. So why is there no “JSON Schema” tab next to Paste and Samples?

The short answer: the site is called JSON to Types, not Schema to Types, and the workflow is different enough that it deserves its own tool rather than a mode on this one.

The longer answer is worth unpacking, because the choice between “generate from schema” and “generate from samples” shows up in almost every API project.

What you have in hand

Consider what you actually have when reaching for a type generator:

  • A real API response. You hit curl or the network tab, you have the payload in front of you. The source of truth is what the server actually sent.
  • A JSON Schema, OpenAPI spec, or GraphQL schema. The API team published a contract. You are generating types up front, before hitting the endpoint. Less common but real in enterprise settings.
  • Both. A schema from the docs and a response in your terminal. They disagree about one field. Now what?

These two inputs produce similar types for different reasons, and conflating them hides important information.

A schema is what they promised. A sample is what they sent. Both are useful. Neither is better.

— the house style

Two workflows, two failure modes

A generator fed with samples is learning the API from its behavior. It will never infer a field that was not in the samples, which sometimes means the generated type is narrower than reality — a field the docs mention but your payloads did not include gets left out. The type matches your observed truth, not the full truth.

A generator fed with a schema is transcribing the contract. It will include every field the schema lists, at the width the schema specifies, with whatever nullability and descriptions the schema author chose. If the schema is good, the type is good. If the schema is stale or wrong, you inherit its errors — and you often cannot tell without hitting the endpoint.

In practice, the failure modes are different enough to be worth naming:

  • Schema says a field is required; real response sometimes omits it. You write code assuming the field is always there. Production throws.
  • Schema is out of date; real response includes a new nested object. Your type does not know about it. The data is silently ignored in TypeScript; your Go struct happily unmarshals around it.
  • Schema defines an enum; real response returns a string not in the enum set. Depending on strictness, either your code throws on a legal value or it accepts anything.

None of these are the tool’s fault in isolation. But if the tool presents them as interchangeable tabs — paste samples OR paste schema, same output — users end up not knowing which one to trust, and therefore which workflow they are in.

Why samples is the default

  1. Most people have samples, not schemas. A curl’d response is immediate. Finding a schema (if the API publishes one at all) takes research.
  2. Samples surface discriminants and literals naturally (see the string is a lie essay). Schemas often declare these as type: string unless the author listed the allowed values explicitly.
  3. Samples expose nullability honestly — if your sample has null, the type says null. Schemas sometimes misremember which fields are nullable.
  4. Samples are testable. You can paste a real payload and see the inferred type match what you need. Schemas require trusting someone you cannot talk to.

For the schema-input case, quicktype’s CLI does this well, and tools like json-schema-to-typescript handle the narrow case with more focused options. Rather than build a second mode into this site that would encourage mixing the two workflows, the recommendation is simple: keep them separate. Use this site for samples. Use a schema-specific tool for schemas.

The third path

There is a third path that comes up: generating both types (from schema) and a validator (from the same schema or from samples) and reconciling them at the boundary. That is a reasonable architecture — Typebox pushes this direction. But it is a decision that belongs in your project, not in the conversion tool.

Practical order of operations

If you are about to open some tool and generate types for an API you have not worked with before:

  • Grab a sample first. Paste it here. See what shape the real response has.
  • Then check if the API publishes a schema. Compare the two. Most of the time they agree; the disagreements are interesting and usually point at edge cases the schema author missed.
  • Only trust the schema for fields your samples did not cover. If the samples saw it, the samples are the truth. If the samples missed it, the schema is your best guess at what is possible.

The goal is a type that encodes what the API actually does, not what its author documented it as doing three versions ago. Samples get you there faster than schemas, most of the time. That is why this tool starts there.