JSON Schema — The Complete Guide
JSON Schema is a declarative language for describing the structure and constraints of JSON data. It lets you define what valid JSON looks like, then validate documents against that definition automatically. If you work with REST APIs, configuration files, event payloads, or any system that exchanges JSON, understanding JSON Schema is a foundational skill.
This guide covers everything from the basics of validation keywords to advanced composition techniques, with practical examples you can use immediately.
What Is JSON Schema?
At its core, a JSON Schema is itself a JSON document that describes the shape of other JSON documents. It specifies which properties are expected, what types they should be, which values are allowed, and how nested structures should look.
JSON Schema serves several purposes in modern development workflows. It powers form generation in frontend applications, validates request and response bodies in API gateways, documents data contracts between services, and drives code generation in typed languages. Because the schema is machine-readable, the same definition can be reused across documentation, validation, testing, and code generation.
JSON Schema Drafts Explained
JSON Schema has evolved through several specification drafts. Each draft refines the vocabulary and adds capabilities. Understanding which draft you are targeting matters because validator libraries may not support every draft equally.
Draft 4
Published in 2013, Draft 4 was the first widely adopted version. It introduced the core vocabulary: type, properties, required, items, enum, and composition keywords like allOf, anyOf, and oneOf. Many existing schemas still target Draft 4 because of its broad tool support.
Draft 6 and Draft 7
Draft 6 (2017) added const, contains, propertyNames, and the examples keyword. Draft 7 (2018) built on this with if/then/else conditional logic, readOnly and writeOnly annotations, and the contentMediaType and contentEncoding keywords. Draft 7 remains one of the most commonly used drafts in production systems today.
2019-09 and 2020-12
The specification moved to date-based naming starting with 2019-09. This draft introduced a vocabulary system that lets schema authors define custom keyword sets, the $anchor keyword for reliable in-document references, and unevaluatedProperties/unevaluatedItems for stricter validation when composing schemas.
Draft 2020-12 is the latest stable specification. It refined the vocabulary system, replaced the $recursiveRef mechanism with the simpler $dynamicRef, and introduced prefixItems for tuple validation (replacing the overloaded items array form). If you are starting a new project, 2020-12 is the recommended target.
Core Validation Keywords
The following keywords form the foundation of every JSON Schema. Mastering them covers the vast majority of real-world validation needs.
type
The type keyword constrains a value to one or more JSON types: string, number, integer, boolean, array, object, or null.
{`{
"type": "string"
}`}
You can allow multiple types by passing an array: "type": ["string", "null"] allows either a string or null.
properties, required, and additionalProperties
For objects, properties defines the expected keys and their sub-schemas. The required array lists which properties must be present. Setting additionalProperties to false rejects any keys not listed in properties.
{`{
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name", "email"],
"additionalProperties": false
}`} enum and const
The enum keyword restricts a value to a fixed set of allowed values. The const keyword (Draft 6+) restricts it to a single exact value.
{`{
"type": "string",
"enum": ["active", "inactive", "suspended"]
}`} String Validation
Strings can be constrained with minLength, maxLength, pattern (a regular expression), and format. Built-in formats include email, uri, date, date-time, uuid, ipv4, ipv6, and others. Note that format validation behavior varies by validator; some treat it as an annotation only unless configured otherwise.
Numeric Validation
Numbers and integers support minimum, maximum, exclusiveMinimum, exclusiveMaximum, and multipleOf.
{`{
"type": "number",
"minimum": 0,
"exclusiveMaximum": 100,
"multipleOf": 0.01
}`} Array Validation
Arrays can be validated with items (schema for each element), minItems, maxItems, uniqueItems, and contains (at least one element must match). In Draft 2020-12, prefixItems validates tuple-like arrays where each position has its own schema.
{`{
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}`} Composition Keywords
Composition keywords let you combine multiple schemas. They are essential for modeling complex data types and keeping schemas modular.
allOf
A value must satisfy every schema in the allOf array. This is commonly used for schema inheritance, where a base schema defines shared properties and specialized schemas add their own.
{`{
"allOf": [
{ "$ref": "#/$defs/baseAddress" },
{
"properties": {
"country": { "const": "US" },
"state": { "type": "string", "minLength": 2, "maxLength": 2 }
},
"required": ["state"]
}
]
}`} anyOf
A value must satisfy at least one schema in the anyOf array. Use this when multiple valid shapes exist and you want permissive matching.
oneOf
A value must satisfy exactly one schema in the oneOf array. This is useful for discriminated unions, where a type field determines which schema applies.
{`{
"oneOf": [
{
"properties": {
"type": { "const": "credit_card" },
"card_number": { "type": "string", "pattern": "^[0-9]{16}$" }
},
"required": ["type", "card_number"]
},
{
"properties": {
"type": { "const": "bank_transfer" },
"iban": { "type": "string" }
},
"required": ["type", "iban"]
}
]
}`} not
The not keyword inverts a schema. A value is valid only if it does not match the given schema. Use sparingly, as not schemas can be hard to reason about and produce confusing error messages.
References with $ref
As schemas grow, duplication becomes a problem. The $ref keyword lets you reference a schema defined elsewhere, either within the same document or in an external file.
{`{
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"postal_code": { "type": "string" }
},
"required": ["street", "city"]
}
},
"type": "object",
"properties": {
"billing_address": { "$ref": "#/$defs/address" },
"shipping_address": { "$ref": "#/$defs/address" }
}
}`}
The $defs keyword (or definitions in older drafts) is the conventional place to put reusable sub-schemas. References can also point to external URIs, enabling shared schema registries across teams and services.
Conditional Schemas
Draft 7 introduced if/then/else for conditional validation. If the value matches the if schema, it must also match the then schema; otherwise, it must match the else schema (if provided).
{`{
"type": "object",
"properties": {
"country": { "type": "string" }
},
"if": {
"properties": { "country": { "const": "US" } }
},
"then": {
"properties": {
"zip_code": { "type": "string", "pattern": "^[0-9]{5}(-[0-9]{4})?$" }
},
"required": ["zip_code"]
},
"else": {
"properties": {
"postal_code": { "type": "string" }
},
"required": ["postal_code"]
}
}`} Conditional schemas are powerful but can make schemas harder to read. Use them when a property's validation depends on the value of another property.
Real-World Use Cases
API Request and Response Validation
JSON Schema is the backbone of the OpenAPI specification. Every request body and response body can be defined with a JSON Schema, enabling automatic validation at the API gateway level. This catches malformed payloads before they reach your application code, reducing error handling boilerplate and improving security. You can convert existing JSON payloads into schemas automatically with the JSON to Schema tool.
Configuration File Validation
Tools like VS Code, ESLint, and Kubernetes use JSON Schema to validate configuration files. By associating a schema with a config file, editors provide autocompletion, inline documentation, and real-time error highlighting. This dramatically reduces configuration errors.
Event-Driven Architectures
In microservice environments, JSON Schema defines the contract for messages on event buses like Kafka or RabbitMQ. A schema registry stores versioned schemas, and producers and consumers validate messages against the registered schema. This prevents breaking changes from propagating silently through the system.
Code Generation
JSON Schema can drive code generation for TypeScript interfaces, Python dataclasses, Go structs, and more. This ensures that the types in your application code stay in sync with the data contracts they represent.
Testing with Mock Data
Given a JSON Schema, tools can generate realistic mock data that conforms to the schema. This is invaluable for testing APIs, populating development databases, and building prototypes without a live backend. Our Mock Generator does exactly this, producing valid sample payloads from any schema you provide.
Best Practices
- Start with the latest draft: Use 2020-12 for new projects. It has the cleanest semantics and the most capable features.
- Use $ref liberally: Extract reusable definitions into
$defsto keep schemas DRY and maintainable. - Add descriptions: The
descriptionkeyword is an annotation that documentation tools and editors display. Treat it like code comments. - Set additionalProperties to false: In API schemas, rejecting unknown properties catches typos and prevents accidental data leakage.
- Validate your schemas: A schema is JSON, and it can itself be validated against the JSON Schema meta-schema. Use the JSON Validator to check your schemas before deploying them.
- Version your schemas: As your data model evolves, version your schemas so that consumers can migrate at their own pace.
- Provide examples: The
exampleskeyword helps consumers understand the expected shape at a glance.
Summary
JSON Schema is a versatile tool that brings structure, validation, and documentation to JSON data. Whether you are defining API contracts, validating configuration files, or generating types for your codebase, a well-crafted schema eliminates entire categories of bugs and makes your system easier to understand.
Start with the core keywords, use composition to keep schemas modular, and leverage tooling to validate, generate, and test against your schemas throughout the development lifecycle.
Try it yourself
Paste a JSON Schema and a JSON document to validate instantly. See detailed error messages with exact paths to invalid values.
Open JSON Schema Validator