JSON Schema Draft Comparison: 04 vs 07 vs 2019-09 vs 2020-12
If you have worked with JSON Schema for more than a year, you have probably encountered the dreaded "draft incompatibility" problem. A schema written for Draft 04 fails to validate in a Draft 07 validator, and you have no idea why.
Here is what changed between each draft and how to handle the transition.
Draft 04 (2013)
The first widely adopted JSON Schema draft. Still in active use in many tools and specifications.
Key syntax:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"id": "http://example.com/schemas/user.json",
"type": "object",
"properties": {
"name": { "type": "string" }
},
"required": ["name"]
}
Limitations:
idis a keyword (conflicts with the HTML attributeid)- No
if/then/elsesupport exclusiveMinimumandexclusiveMaximumare booleans, not numbers- No
constkeyword
Draft 07 (2018)
The most significant update. Added the features developers were requesting most.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "http://example.com/schemas/user.json",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": {
"type": "integer",
"exclusiveMinimum": 0, // Now accepts a number instead of boolean!
"exclusiveMaximum": 150
},
"role": {
"type": "string",
"const": "admin" // New in draft 07!
},
"discount": {
"type": "number",
"if": { "exclusiveMaximum": 100 },
"then": { "multipleOf": 5 },
"else": { "multipleOf": 10 }
}
},
"required": ["name"]
}
New features:
$idreplacesid(backward-incompatible change!)if/then/elseconditional validationconstkeyword (a single allowed value)exclusiveMinimum/exclusiveMaximumnow accept numberscontentEncodingandcontentMediaTypepropertyNamesvalidation
The id → $id breaking change is the most common migration issue. Schema that used "id": "http://..." in Draft 04 must use "$id": "http://..." in Draft 07.
On Stack Overflow, the question about draft differences has 30,000 views. The most common migration mistake is keeping the old id keyword in a Draft 07 schema.
Draft 2019-09
Added vocabularies as a formal extension mechanism:
{
"$schema": "https://json-schema.org/draft/2019-09/schema",
"$id": "https://example.com/schema",
"$vocabulary": {
"https://json-schema.org/draft/2019-09/vocab/core": true,
"https://json-schema.org/draft/2019-09/vocab/applicator": true,
"https://json-schema.org/draft/2019-09/vocab/validation": true
},
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"deprecated": true, // New: marks a property as deprecated
"comment": "Use 'contact_email' instead" // New: schema annotations
}
},
"unevaluatedProperties": false // New: catches additional properties
}
Key additions:
$vocabularyfor declaring which vocabularies are useddeprecatedannotationcommentannotationunevaluatedPropertiesandunevaluatedItems$recursiveRefand$recursiveAnchorfor recursive schemas$defsreplacesdefinitions(old keyword still supported)
Draft 2020-12 (Latest)
The current standard:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schema",
"$defs": { // $defs replaces definitions (official)
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
},
"type": "object",
"properties": {
"shipping": { "$ref": "#/$defs/address" },
"billing": { "$ref": "#/$defs/address" }
},
"prefixItems": [ // New: replaces tuple validation in items
{ "type": "string" },
{ "type": "integer" },
{ "type": "string" }
],
"items": { "type": "string" } // Now only for uniform arrays
}
Key changes from 2019-09:
prefixItemsreplacesitemsfor tuple validation$defsis the official replacement fordefinitions(was unofficially supported in 2019-09)$refno longer applies sibling keywords (breaking change!)- New format system:
formatvalidation is opt-in by default
Migration Decision
| Draft | When to Use |
|---|---|
| Draft 04 | Legacy systems, tools that only support 04 |
| Draft 07 | Most compatibility, widest tool support |
| 2019-09 | When you need vocabularies or unevaluatedProperties |
| 2020-12 | New projects with modern validators |
My recommendation: Use Draft 07 for maximum compatibility (every major validator supports it). Use 2020-12 for new projects if your validation library supports it.
Common Migration Issues
// Draft 04 →
{
"id": "http://example.com/schema",
"exclusiveMinimum": true,
"minimum": 0
}
// Draft 07 → (breaking changes)
{
"$id": "http://example.com/schema", // id → $id
"exclusiveMinimum": 0 // boolean → number
}
// Draft 2019-09 →
{
"definitions": { "user": { ... } }
}
// Draft 2020-12 →
{
"$defs": { "user": { ... } } // $defs replaces definitions
}
Related Searches
- json schema draft 04 vs 07
- json schema draft 2020-12 changes
- json schema migration draft 04 to 07
- json schema id vs $id
- json schema breaking changes
- json schema draft comparison
- json schema which draft to use
- json schema versions history
Frequently Asked Questions
Which JSON Schema draft should I use for a new project?
Use Draft 07 for broadest tool compatibility. Use Draft 2020-12 if your validator supports it and you need the latest features (prefixItems, $defs, unevaluatedProperties). Draft 04 should only be used for legacy compatibility.
What is the most breaking change between drafts?
The id → $id rename between Draft 04 and Draft 07. In Draft 04, the schema identifier was "id". In Draft 07 and later, it is "$id". Using the wrong keyword means the schema ID is not recognized, which breaks $ref resolution.
Can I use a Draft 07 schema with a Draft 2020-12 validator?
Yes, if you set the $schema keyword correctly. Validators use $schema to determine which draft rules to apply. A Draft 07 schema with $schema: "http://json-schema.org/draft-07/schema#" will work in a Draft 2020-12 validator.
What happened to definitions in newer drafts?
definitions was replaced by $defs starting in Draft 2019-09. The old definitions keyword is still supported for backward compatibility but is deprecated. New schemas should use $defs.
Final Thoughts
JSON Schema draft compatibility is a manageable problem if you (1) always include the $schema keyword, (2) use Draft 07 unless you need a specific newer feature, and (3) test your schemas with your specific validator version.