JSON Schema Validation: A Practical Guide

May 26, 202612 min read

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)
  • title and description: Human-readable documentation
  • type: 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:

  • username and email MUST be present
  • age is optional
  • If age is 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

KeywordApplies ToDescription
typeanySpecifies data type
enumanyMust be one of the specified values
constanyMust be exactly this value
minimum / maximumnumberRange constraints
minLength / maxLengthstringLength constraints
patternstringRegex pattern
minItems / maxItemsarrayArray length constraints
minProperties / maxPropertiesobjectObject property count
requiredobjectRequired property names
propertiesobjectProperty schemas
additionalPropertiesobjectControl extra properties
itemsarraySchema for array items

Best Practices

  1. Always specify $schema to ensure consistent behavior across implementations
  2. Use additionalProperties: false for strict object validation
  3. Provide meaningful title and description for documentation
  4. Use allErrors: true in Ajv during development to see all validation errors
  5. Test your schemas with both valid and invalid data
  6. Use $ref for reusable components to avoid duplication
  7. Consider using examples keyword to document expected data formats