JSON Schema
JSON is everywhere — API requests and responses, configuration files, events, documents in databases — and JSON itself says nothing about what a valid document looks like. JSON Schema is a vocabulary, written in JSON, for describing that shape: which properties exist, their types, which are required, allowed values, ranges, and patterns. Validators in nearly every language check documents against a schema and report precisely what is wrong.
Beyond validation, schemas power documentation, code generation, form generation, editor autocompletion for configuration files, and the request and response definitions in OpenAPI 3.1, which is fully aligned with JSON Schema. A schema written once can serve as the contract between teams, services, and tools.
TL;DR
- A schema describes valid JSON with keywords such as
type,properties,required,enum,minimum, andpattern. - Objects accept unknown properties by default; use
additionalProperties: falseorunevaluatedProperties: falseto reject them. - Reuse definitions with
$defsand$ref; combine schemas withallOf,anyOf, andoneOf. - Declare the dialect with
$schema; Draft 2020-12 is the current release, and OpenAPI 3.1 uses it. format(such asemailordate-time) is an annotation by default — enable format validation explicitly.- Validate at boundaries with mature validators: Ajv (JavaScript), jsonschema (Python), and equivalents elsewhere.
Quick Example
A schema for a signup request:
Validating with Ajv in Node.js, collecting every error rather than stopping at the first:
Core Concepts
Core Keywords
Open by Default
A schema only constrains what it mentions. Without additionalProperties: false, a document with a misspelled property — "emial" — passes validation, and the real email property is simply missing (caught only if it is required). Decide deliberately whether each object is closed (reject unknown properties) or open (allow extension).
Composition
allOf: valid against every subschema — used to combine a base schema with extensions.anyOf: valid against at least one subschema.oneOf: valid against exactly one — useful for tagged unions, but error messages get confusing; pair it with a discriminatingconstproperty.if/then/else: conditional rules, such as requiringvatNumberwhencountryis in the EU.
additionalProperties only sees properties declared in the same schema object, so it breaks composition with allOf. unevaluatedProperties: false (since Draft 2019-09) looks at properties evaluated by any subschema and is the correct way to close composed schemas.
Dialects and Versions
Always declare $schema so validators apply the right rules. OpenAPI 3.0 used a modified subset of Draft 4/5 (with nullable); OpenAPI 3.1 uses real JSON Schema, so type: ["string", "null"] replaces nullable: true.
Validation in Practice
Python, with format checking enabled explicitly:
Where validation belongs:
- API boundaries: validate requests before business logic; frameworks generated from OpenAPI often do this automatically.
- Configuration files: reject bad config at startup with clear messages; editors such as VS Code use schemas (including from SchemaStore) for autocompletion.
- Event and message contracts: validate in producer tests and consumer pipelines. See Data Contracts and Schema Evolution.
Schemas and Code
Choose one source of truth. Teams in TypeScript often define Zod schemas and export JSON Schema; Python teams often use Pydantic models; polyglot organizations often keep JSON Schema or OpenAPI files as the contract and generate code in each language. See OpenAPI and API Contract Generation.
Evolving Schemas
Version schemas through $id URLs, run compatibility checks in CI, and prefer additive changes.
Best Practices
Validate Early and Return Every Error
Validate at the boundary and report all problems with their paths in one response, so clients can fix everything at once.
Close Objects That Represent Commands
Requests and configuration usually benefit from rejecting unknown properties, which catches typos. Event payloads consumed by many services are often better left open for forward compatibility.
Keep Schemas Readable
Factor shared definitions into $defs, add description to every non-obvious property, and include examples. Schemas are documentation as much as validation.
Do Not Treat Validation as Authorization
A valid document is well-formed, not permitted. Ownership, permissions, and business rules still belong in application logic.
Common Mistakes
Assuming Formats Are Checked
additionalProperties With allOf
Mixing Dialects
FAQ
What is JSON Schema used for?
Describing and validating the structure of JSON documents — API requests and responses, configuration files, events, and stored documents — and generating documentation, forms, and code from those descriptions.
Which JSON Schema version should I use?
Draft 2020-12 for new work, especially with OpenAPI 3.1. Use an older draft only when a tool you depend on requires it, and declare the version with $schema.
What is the difference between anyOf and oneOf?
anyOf passes if at least one subschema matches; oneOf requires exactly one. oneOf fails when a document matches two subschemas, which is useful for strict unions but produces harder-to-read errors.
Does JSON Schema validate formats like email?
Only when format assertion is enabled. Since Draft 2019-09, format is an annotation by default; most validators offer an option or plugin to enforce common formats.
How does JSON Schema relate to OpenAPI?
OpenAPI describes HTTP APIs and uses schemas for request and response bodies. OpenAPI 3.1 uses JSON Schema Draft 2020-12 directly; OpenAPI 3.0 used a modified subset of an older draft.
Related Topics
- API Design — Designing the contracts schemas describe
- OpenAPI and API Contract Generation — Schemas in API descriptions
- Zod — TypeScript-first schemas that export JSON Schema
- FastAPI with Pydantic — Python models that generate JSON Schema
- Data Contracts and Schema Evolution — Evolving shared schemas safely
- Protocol Buffers — A binary schema format for services