JSON Schema Validation: A Practical Guide
What is JSON Schema?
JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It provides a contract for your JSON data — defining what structure is expected, what types are allowed, and what constraints must be met. Think of it as a TypeScript interface for your JSON data, but one that can be validated at runtime.
JSON Schema is particularly valuable when:
- Building APIs that accept JSON payloads
- Validating configuration files
- Ensuring data integrity in databases
- Providing documentation for expected data structures
The current stable version is Draft 2020-12, but you'll also encounter Draft-07 and Draft-04 in the wild.
Basic Schema Structure
A JSON Schema document is itself a JSON document with a specific structure:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/product.json",
"title": "Product",
"description": "A product in the catalog",
"type": "object",
"properties": {
"name": { "type": "string" },
"price": { "type": "number" }
}
}
Key fields:
$schema: Identifies which version of JSON Schema to use$id: A unique identifier for this schema (often a URL)titleanddescription: Human-readable documentationtype: The data type (string, number, integer, object, array, boolean, null)
Type Validation
JSON Schema supports these basic types:
{
"type": "string",
"type": "number",
"type": "integer",
"type": "boolean",
"type": "object",
"type": "array",
"type": "null"
}
String Validation
{
"type": "string",
"minLength": 3,
"maxLength": 50,
"pattern": "^[A-Za-z ]+$",
"format": "email"
}
Built-in formats: date-time, date, time, email, idn-email, hostname, idn-hostname, ipv4, ipv6, uri, uri-reference, iri, iri-reference, uri-template, json-pointer, relative-json-pointer, regex
Number and Integer Validation
{
"type": "number",
"minimum": 0,
"maximum": 100,
"exclusiveMinimum": 0,
"exclusiveMaximum": 100,
"multipleOf": 5
}
Note: integer is a separate type that only accepts whole numbers.
Object Properties and Required Fields
The properties keyword defines what keys are allowed in an object, and required specifies which ones must be present.
{
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 3,
"maxLength": 20,
"pattern": "^[a-zA-Z0-9_]+$"
},
"email": {
"type": "string",
"format": "email"
},
"age": {
"type": "integer",
"minimum": 18
}
},
"required": ["username", "email"]
}
This schema means:
usernameandemailMUST be presentageis optional- If
ageis present, it must be an integer >= 18
Additional Properties
By default, JSON Schema allows additional properties not mentioned in properties. To restrict this:
{
"type": "object",
"properties": {
"name": { "type": "string" }
},
"additionalProperties": false
}
Or specify a schema for additional properties:
{
"type": "object",
"properties": {
"name": { "type": "string" }
},
"additionalProperties": { "type": "string" }
}
Nested Objects and Arrays
Nested Objects
{
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zipCode": { "type": "string", "pattern": "^[0-9]{5}$" }
},
"required": ["street", "city"]
}
},
"required": ["firstName", "lastName"]
}
}
}
Array Validation
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"name": { "type": "string" }
}
},
"minItems": 1,
"maxItems": 100,
"uniqueItems": true
}
For arrays with different item types at different positions, use prefixItems:
{
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "number" },
{ "type": "boolean" }
],
"items": false
}
Pattern Properties and Property Names
Pattern Properties
Validate property names that match a regex pattern:
{
"type": "object",
"patternProperties": {
"^x-": { "type": "string" },
"^data-[a-z]+$": { "type": "integer" }
}
}
This allows any property starting with x- (like x-custom-header) to be a string.
Property Names Validation
Validate that all property names meet certain criteria:
{
"type": "object",
"propertyNames": {
"pattern": "^[a-z][a-zA-Z0-9]*$"
}
}
Conditional Schemas: if/then/else
JSON Schema supports conditional validation:
{
"type": "object",
"properties": {
"country": { "type": "string" },
"zipCode": { "type": "string" }
},
"if": {
"properties": { "country": { "const": "US" } }
},
"then": {
"properties": { "zipCode": { "pattern": "^[0-9]{5}(-[0-9]{4})?$" } }
},
"else": {
"properties": { "zipCode": { "type": "string" } }
}
}
Logic: If country is "US", then zipCode must match the US format. Otherwise, zipCode can be any string.
Reusing Schemas with $ref
The $ref keyword allows you to reference other schemas, enabling reuse and modularity.
Internal References
{
"type": "object",
"definitions": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" }
}
}
},
"properties": {
"billingAddress": { "$ref": "#/definitions/address" },
"shippingAddress": { "$ref": "#/definitions/address" }
}
}
External References
{
"type": "object",
"properties": {
"user": {
"$ref": "https://example.com/schemas/user.json"
}
}
}
Complete Example: User Registration
Here's a comprehensive schema for a user registration API:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/registration.json",
"title": "User Registration",
"description": "Schema for user registration payload",
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 3,
"maxLength": 30,
"pattern": "^[a-zA-Z0-9_]+$"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 255
},
"password": {
"type": "string",
"minLength": 8,
"maxLength": 128,
"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]+$"
},
"age": {
"type": "integer",
"minimum": 13,
"maximum": 120
},
"termsAccepted": {
"type": "boolean",
"const": true
}
},
"required": ["username", "email", "password", "termsAccepted"],
"additionalProperties": false
}
Validation with Ajv
Ajv (Another JSON Schema Validator) is the most popular JSON Schema validator for JavaScript.
Installation
npm install ajv ajv-formats
Basic Usage
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv({ allErrors: true });
addFormats(ajv); // Adds format validators like "email", "uri", etc.
const schema = {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'integer', minimum: 0 }
},
required: ['name']
};
const validate = ajv.compile(schema);
const data = { name: 'John', age: 30 };
const valid = validate(data);
if (!valid) {
console.log('Validation errors:', validate.errors);
}
Online Validation
You can also validate JSON against schemas using online tools or our JSON Formatter tool.
Common Validation Keywords Reference
| Keyword | Applies To | Description |
|---|---|---|
type | any | Specifies data type |
enum | any | Must be one of the specified values |
const | any | Must be exactly this value |
minimum / maximum | number | Range constraints |
minLength / maxLength | string | Length constraints |
pattern | string | Regex pattern |
minItems / maxItems | array | Array length constraints |
minProperties / maxProperties | object | Object property count |
required | object | Required property names |
properties | object | Property schemas |
additionalProperties | object | Control extra properties |
items | array | Schema for array items |
Best Practices
- Always specify
$schemato ensure consistent behavior across implementations - Use
additionalProperties: falsefor strict object validation - Provide meaningful
titleanddescriptionfor documentation - Use
allErrors: truein Ajv during development to see all validation errors - Test your schemas with both valid and invalid data
- Use
$reffor reusable components to avoid duplication - Consider using
exampleskeyword to document expected data formats
Related Tools
- JSON Formatter — Format and pretty-print JSON