JSON Schema Validation: The Definitive Guide
JSON Schema is a vocabulary for annotating and validating JSON documents. It provides a contract for your data β defining what shape it should have, what types are acceptable, and which fields are required. If you work with APIs, configuration files, or any structured data, JSON Schema is an essential tool in your arsenal.
Why Use JSON Schema?
Without validation, JSON data is a wild card. You might receive an age field as a string, a missing email, or a date in the wrong format. JSON Schema catches these issues at the boundary, before bad data propagates through your system.
Key benefits:
- API contract enforcement: Validate request and response bodies automatically
- Documentation: The schema serves as living documentation for your data structures
- Code generation: Tools can generate types, forms, and database schemas from JSON Schema
- Testing: Validate test fixtures and mock data against the schema
Basic Schema Structure
Every JSON Schema starts with a few standard properties:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/user.schema.json",
"title": "User",
"description": "A registered user in the system",
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 }
},
"required": ["id", "name", "email"]
}
Use our JSON Formatter to beautify your schemas for readability before sharing with your team.
Type Constraints
JSON Schema supports six primitive types:
| Type | Description | Example |
|---|---|---|
string | Text data | "hello" |
number | Any numeric value | 3.14 |
integer | Whole numbers only | 42 |
boolean | True or false | true |
array | Ordered list | [1, 2, 3] |
object | Key-value pairs | {"key": "value"} |
null | Null value | null |
String Constraints
{
"type": "string",
"minLength": 1,
"maxLength": 255,
"pattern": "^[A-Za-z0-9]+$",
"format": "email"
}
Built-in formats include: email, uri, date, date-time, ipv4, ipv6, uuid, and hostname.
Number Constraints
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMinimum": 0,
"multipleOf": 0.01
}
Array Validation
Arrays can be validated for their items, length, and uniqueness:
{
"type": "array",
"items": { "type": "string", "format": "email" },
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
}
For tuple validation (fixed-length arrays with specific types per position):
{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" },
{ "type": "boolean" }
],
"items": false
}
Object Validation
Objects support property validation, required fields, and additional property control:
{
"type": "object",
"properties": {
"name": { "type": "string" },
"role": { "enum": ["admin", "editor", "viewer"] }
},
"required": ["name", "role"],
"additionalProperties": false
}
Setting additionalProperties to false is a strict mode that rejects any properties not defined in the schema. This is particularly useful for APIs where you want to catch typos in field names.
Composition Keywords
JSON Schema supports combining schemas using logical operators:
allOf (AND)
The data must be valid against all of the subschemas:
{
"allOf": [
{ "$ref": "#/$defs/baseUser" },
{ "properties": { "role": { "const": "admin" } } }
]
}
oneOf (XOR)
The data must be valid against exactly one of the subschemas:
{
"oneOf": [
{ "properties": { "type": { "const": "email" }, "email": { "type": "string" } } },
{ "properties": { "type": { "const": "phone" }, "phone": { "type": "string" } } }
]
}
anyOf (OR)
The data must be valid against at least one of the subschemas.
Conditional Validation
The if/then/else keywords enable conditional validation:
{
"type": "object",
"properties": {
"country": { "type": "string" },
"postalCode": { "type": "string" }
},
"if": {
"properties": { "country": { "const": "US" } }
},
"then": {
"properties": { "postalCode": { "pattern": "^[0-9]{5}(-[0-9]{4})?$" } }
},
"else": {
"properties": { "postalCode": { "pattern": "^[A-Z0-9 -]+$" } }
}
}
References and Reuse
Use $ref to reference reusable definitions:
{
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string" }
},
"required": ["street", "city", "country"]
}
},
"type": "object",
"properties": {
"billingAddress": { "$ref": "#/$defs/address" },
"shippingAddress": { "$ref": "#/$defs/address" }
}
}
Validation in Practice
JavaScript (Ajv)
const Ajv = require('ajv');
const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) console.log(validate.errors);
Python (jsonschema)
from jsonschema import validate, ValidationError
try:
validate(instance=data, schema=schema)
except ValidationError as e:
print(f"Validation error: {e.message}")
For quick validation without setting up a development environment, paste your JSON and schema into our JSON Validator.
Real-World Example: API Response Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": { "enum": ["success", "error"] },
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" },
"createdAt": { "type": "string", "format": "date-time" }
},
"required": ["id", "name"]
}
},
"pagination": {
"type": "object",
"properties": {
"page": { "type": "integer", "minimum": 1 },
"perPage": { "type": "integer", "minimum": 1, "maximum": 100 },
"total": { "type": "integer", "minimum": 0 }
}
}
},
"required": ["status", "data"]
}
FAQ
What is the difference between JSON Schema draft-07 and 2020-12?
Draft 2020-12 introduced several improvements: prefixItems replaced the tuple form of items, $dynamicRef enables more flexible references, and vocabulary support allows custom extensions. For new projects, use 2020-12. For compatibility with older tools, draft-07 is still widely supported.
Can JSON Schema validate nested JSON within a string field?
No, directly. JSON Schema validates the structure of parsed JSON. If a field contains a JSON string, you would need to parse it first and validate separately. Some validators offer custom format validators that can handle this, but it is not part of the standard.
Related Resources
- JSON Formatter β Format and beautify your JSON schemas
- How to Validate JSON β Common JSON validation errors and fixes
- JSON API Design Patterns β Building better REST APIs with JSON