JSON Schema, from the basics to the parts nobody explains.
Every keyword you reach for, ordered from beginner to advanced, with copyable recipes for conditionals, composition, and dynamic references. Jump ahead with the on-this-page menu, or filter to find a keyword fast.
The shape of a schema and the everyday constraints you reach for first.
Core keywords
What you'll learn: how a schema is structured, how to declare the dialect, and the handful of keywords you'll use in almost every schema.
{
"$schema": "https://json-schema.org/draft/2020-12/schema"
}{
"type": ["string", "null"]
}{
"status": { "enum": ["active", "archived"] },
"kind": { "const": "rusl/schema" }
}Strings and numbers
What you'll learn: the constraints that bound text and numeric values — lengths, patterns, named formats, ranges, and steps.
{
"type": "string",
"minLength": 1,
"maxLength": 280
}{
"type": "string",
"pattern": "^[a-z][a-z0-9-]*$"
}{
"type": "string",
"format": "email"
}{
"type": "integer",
"minimum": 0,
"maximum": 100
}{
"type": "number",
"exclusiveMinimum": 0
}{
"type": "number",
"multipleOf": 0.01
}Objects
What you'll learn: how to describe an object's shape — which properties it has, which are required, and what to do with everything else.
{
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" }
}
}{
"type": "object",
"properties": {
"id": { "type": "string" }
},
"required": ["id"]
}{
"type": "object",
"properties": {
"id": { "type": "string" }
},
"additionalProperties": false
}{
"type": "object",
"patternProperties": {
"^x-": { "type": "string" }
}
}{
"type": "object",
"propertyNames": {
"pattern": "^[a-z_]+$"
}
}Arrays
What you'll learn: how to validate lists, fixed-shape tuples, and "must contain" rules.
{
"type": "array",
"items": { "type": "string" }
}{
"type": "array",
"prefixItems": [
{ "type": "number" },
{ "type": "string" }
],
"items": false
}{
"type": "array",
"minItems": 1,
"maxItems": 10
}{
"type": "array",
"uniqueItems": true
}{
"type": "array",
"contains": { "type": "number" },
"minContains": 2
}Composition and conditional logic: schemas that react to the data they validate.
Combining schemas
What you'll learn: how to build a schema out of other schemas with boolean logic — the foundation for reuse and variants.
{
"allOf": [
{ "type": "object", "properties": { "id": { "type": "string" } } },
{ "required": ["id"] }
]
}{
"anyOf": [
{ "type": "string" },
{ "type": "number" }
]
}{
"oneOf": [
{ "type": "string", "maxLength": 5 },
{ "type": "number" }
]
}{
"not": { "type": "null" }
}Conditionals and dependencies
What you'll learn: rules that react to the data itself — how to require or constrain a field only in certain cases.
{
"type": "object",
"properties": {
"country": { "type": "string" },
"postcode": { "type": "string" }
},
"if": {
"properties": { "country": { "const": "US" } },
"required": ["country"]
},
"then": { "required": ["postcode"] }
}{
"type": "object",
"dependentRequired": {
"credit_card": ["billing_address"]
}
}{
"type": "object",
"properties": {
"payment_method": { "type": "string" }
},
"dependentSchemas": {
"credit_card": {
"required": ["billing_address"],
"properties": {
"billing_address": { "type": "string" },
"payment_method": { "const": "card" }
}
}
}
}Referencing and reuse
What you'll learn: how to define a schema once and reuse it — including how to drop one sub-schema into another with $ref.
{
"$defs": {
"uuid": { "type": "string", "format": "uuid" }
},
"type": "object",
"properties": {
"id": { "$ref": "#/$defs/uuid" }
}
}{
"type": "object",
"properties": {
"id": { "$ref": "#/$defs/uuid" },
"request": {
"$ref": "https://resources.rusl.com/resources/rusl/schemas/context-request"
}
},
"$defs": {
"uuid": { "type": "string", "format": "uuid" }
}
}{
"$id": "https://resources.rusl.com/resources/rusl/schemas/common",
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" }
}
}{
"type": "object",
"properties": {
"billing": { "$ref": "#address" }
},
"$defs": {
"address": {
"$anchor": "address",
"type": "object"
}
}
}The powerful, lesser-known keywords, plus recipes and the gotchas they exist to fix.
Advanced keywords
What you'll learn: the keywords that make 2020-12 strictly more powerful — closing objects after composition, recursive references, and validating content carried inside strings.
{
"allOf": [{ "$ref": "#/$defs/base" }],
"properties": {
"extra": { "type": "string" }
},
"unevaluatedProperties": false,
"$defs": {
"base": {
"type": "object",
"properties": { "id": { "type": "string" } }
}
}
}{
"allOf": [{ "prefixItems": [{ "type": "number" }] }],
"unevaluatedItems": false
}{
"$id": "https://resources.rusl.com/resources/rusl/schemas/tree",
"$ref": "#/$defs/node",
"$defs": {
"node": {
"$dynamicAnchor": "node",
"type": "object",
"properties": {
"value": { "type": "string" },
"children": {
"type": "array",
"items": { "$dynamicRef": "#node" }
}
}
}
}
}{
"type": "string",
"contentMediaType": "application/json",
"contentSchema": {
"type": "object",
"required": ["id"]
}
}{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://resources.rusl.com/resources/rusl/schemas/dialect",
"$vocabulary": {
"https://json-schema.org/draft/2020-12/vocab/core": true,
"https://json-schema.org/draft/2020-12/vocab/validation": true
}
}Recipes
What you'll learn: copy-paste solutions to real problems that aren't obvious from the keyword list alone.
{
"type": "object",
"properties": {
"vehicleType": { "type": ["string", "null"] },
"topSpeed": { "type": "number" }
},
"if": {
"properties": {
"vehicleType": { "not": { "type": "null" } }
},
"required": ["vehicleType"]
},
"then": { "required": ["topSpeed"] }
}{
"type": "object",
"required": ["type"],
"properties": {
"type": { "enum": ["car", "truck"] }
},
"oneOf": [
{
"properties": {
"type": { "const": "car" },
"wheels": { "type": "integer" }
},
"required": ["wheels"]
},
{
"properties": {
"type": { "const": "truck" },
"loadCapacity": { "type": "number" }
},
"required": ["loadCapacity"]
}
]
}{
"type": "object",
"properties": {
"personalId": { "type": "string" },
"companyId": { "type": "string" }
},
"oneOf": [
{ "required": ["personalId"], "not": { "required": ["companyId"] } },
{ "required": ["companyId"], "not": { "required": ["personalId"] } }
]
}{
"allOf": [{ "$ref": "#/$defs/base" }],
"properties": {
"premium": { "type": "boolean" }
},
"unevaluatedProperties": false,
"$defs": {
"base": {
"type": "object",
"properties": { "name": { "type": "string" } }
}
}
}{
"$id": "https://resources.rusl.com/resources/rusl/schemas/category",
"$ref": "#/$defs/node",
"$defs": {
"node": {
"type": "object",
"properties": {
"name": { "type": "string" },
"children": {
"type": "array",
"items": { "$ref": "#/$defs/node" }
}
}
}
}
}{
"type": "object",
"properties": {
"discountCode": {
"type": ["string", "null"],
"minLength": 6,
"pattern": "^[A-Z0-9]+$"
}
}
}{
"type": "object",
"properties": {
"request": {
"$ref": "https://resources.rusl.com/resources/rusl/schemas/context-request"
}
}
}Common gotchas
What you'll learn: the mistakes almost everyone makes once.
{
"$ref": "#/$defs/name",
"minLength": 3,
"$defs": {
"name": { "type": "string" }
}
}