JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It defines what a JSON structure should look like - which fields are required, what types they should be, acceptable value ranges, and much more. If you have ever encountered TypeScript interfaces, Pydantic models in Python, or OpenAPI specifications, you have worked with JSON Schema concepts even if not directly.
JSON Schema serves several purposes: validating API request bodies, documenting the shape of data, generating code (TypeScript types, Python dataclasses), and configuring editors to provide autocomplete for JSON files.
The Basic Structure of a JSON Schema
A JSON Schema is itself a JSON document. At minimum, it contains a $schema declaration and a type:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/person.schema.json",
"title": "Person",
"description": "A person object",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The person's full name"
},
"age": {
"type": "integer",
"description": "Age in years",
"minimum": 0,
"maximum": 150
},
"email": {
"type": "string",
"format": "email"
}
},
"required": ["name", "email"]
}
This schema describes a JSON object with three properties: name (required string), age (optional integer between 0 and 150), and email (required string in email format).
Primitive Type Validation
JSON Schema can validate all six JSON types and add constraints to each:
String Constraints
{
"type": "string",
"minLength": 1,
"maxLength": 100,
"pattern": "^[a-zA-Z]+$",
"format": "email"
}
The format keyword is informational for basic validators but enforced by strict validators. Common formats include: email, uri, date (YYYY-MM-DD), date-time (ISO 8601), uuid, ipv4, and ipv6.
Number Constraints
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMaximum": 100,
"multipleOf": 0.5
}
Use type: "integer" to reject decimals. Use exclusiveMinimum and exclusiveMaximum to exclude the boundary value.
Boolean and Null
{ "type": "boolean" }
{ "type": "null" }
// Allow a value to be either a string or null (nullable)
{ "type": ["string", "null"] }
Object Validation
Object schemas can define properties, required fields, and control whether additional (unlisted) properties are allowed:
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"username": {
"type": "string",
"minLength": 3,
"maxLength": 30,
"pattern": "^[a-z0-9_]+$"
},
"role": {
"type": "string",
"enum": ["admin", "editor", "viewer"]
},
"createdAt": {
"type": "string",
"format": "date-time"
}
},
"required": ["id", "username", "role"],
"additionalProperties": false,
"minProperties": 3,
"maxProperties": 10
}
Setting "additionalProperties": false means the validator rejects any property not listed in properties. This is useful for strict API contracts but can be too rigid for extensible schemas.
Array Validation
{
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
}
The items keyword defines the schema for each element in the array. uniqueItems: true enforces that all array values are distinct (like a Set). You can also use prefixItems (in JSON Schema 2020-12) to define schemas for positional items in a tuple:
{
"type": "array",
"prefixItems": [
{ "type": "string", "description": "First name" },
{ "type": "string", "description": "Last name" },
{ "type": "integer", "description": "Age" }
],
"items": false
}
Reusing Schemas with $ref and $defs
JSON Schema supports schema reuse through references. This avoids duplicating definitions and keeps schemas maintainable:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"Address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{5}$" }
},
"required": ["street", "city"]
}
},
"type": "object",
"properties": {
"name": { "type": "string" },
"billingAddress": { "$ref": "#/$defs/Address" },
"shippingAddress": { "$ref": "#/$defs/Address" }
}
}
Combining Schemas with Logical Keywords
JSON Schema includes Boolean logic operators for combining schemas:
// allOf - must match ALL schemas
{
"allOf": [
{ "type": "object" },
{ "required": ["id"] },
{ "minProperties": 2 }
]
}
// anyOf - must match AT LEAST ONE schema
{
"anyOf": [
{ "type": "string" },
{ "type": "number" }
]
}
// oneOf - must match EXACTLY ONE schema
{
"oneOf": [
{
"properties": { "type": { "const": "circle" }, "radius": { "type": "number" } },
"required": ["type", "radius"]
},
{
"properties": { "type": { "const": "rectangle" }, "width": {}, "height": {} },
"required": ["type", "width", "height"]
}
]
}
// not - must NOT match the schema
{
"not": { "type": "null" }
}
Conditional Validation with if/then/else
JSON Schema supports conditional schema application - useful for discriminated unions where the required fields depend on another field's value:
{
"type": "object",
"properties": {
"paymentMethod": {
"type": "string",
"enum": ["card", "bank_transfer"]
}
},
"if": {
"properties": { "paymentMethod": { "const": "card" } }
},
"then": {
"required": ["cardNumber", "expiryDate", "cvv"]
},
"else": {
"required": ["bankAccountNumber", "routingNumber"]
}
}
Validating JSON with JSON Schema in Code
JavaScript (Ajv library):
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv();
addFormats(ajv);
const schema = {
type: "object",
properties: {
name: { type: "string", minLength: 1 },
email: { type: "string", format: "email" }
},
required: ["name", "email"]
};
const validate = ajv.compile(schema);
const data = { name: "Alice", email: "alice@example.com" };
if (validate(data)) {
console.log("Valid!");
} else {
console.log(validate.errors);
}
Python (jsonschema library):
import json
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"}
},
"required": ["name", "email"]
}
data = {"name": "Alice", "email": "alice@example.com"}
try:
validate(instance=data, schema=schema)
print("Valid!")
except ValidationError as e:
print(f"Invalid: {e.message}")
JSON Schema in OpenAPI
OpenAPI (formerly Swagger) uses JSON Schema to describe request and response bodies. If you have worked with OpenAPI specifications, the schema: sections are JSON Schema:
paths:
/users:
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
email:
type: string
format: email
required:
- name
- email
Discover Your JSON Structure Before Writing a Schema
Use JSON Keyper to extract all key paths from an existing JSON document - it gives you a complete inventory of every field to include in your JSON Schema definition.
Open JSON Keyper