JSON Schema Validation Guide: Enforce Data Structure Like a Pro

When APIs exchange JSON data, things can go wrong fast — missing fields, wrong types, unexpected values. JSON Schema provides a standardised way to define and validate the structure of JSON documents, catching errors before they cause bugs downstream.

What Is JSON Schema?

JSON Schema is a vocabulary that allows you to describe the shape of your JSON data. Think of it as a contract: "this field must be a string", "this number must be between 0 and 100", "this array must have at least one item". It's written in JSON itself, which means it integrates naturally into any JSON-based workflow.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": { "type": "string", "minLength": 1 },
    "age": { "type": "integer", "minimum": 0 },
    "email": { "type": "string", "format": "email" }
  },
  "required": ["name", "email"]
}

This schema says: the data must be an object with a name (non-empty string), an optional age (non-negative integer), and a required email in valid format.

💡 Version Tip: The latest version is Draft 2020-12. If you're starting a new project, use this draft. Older drafts (Draft-07, Draft-04) are still widely supported but lack newer features.

Core Keywords

JSON Schema uses keywords to define constraints. Here are the most important ones:

Type Constraints

{ "type": "string" }      // Must be a string
{ "type": "number" }      // Any number (integer or float)
{ "type": "integer" }     // Whole numbers only
{ "type": "boolean" }     // true or false
{ "type": "array" }       // Must be an array
{ "type": "object" }      // Must be an object
{ "type": "null" }        // Must be null
{ "type": ["string", "null"] }  // String OR null

String Validation

{
  "type": "string",
  "minLength": 1,           // At least 1 character
  "maxLength": 255,         // At most 255 characters
  "pattern": "^[A-Z]{2,3}$" // Regex: 2-3 uppercase letters
}

Number Validation

{
  "type": "number",
  "minimum": 0,            // >= 0
  "maximum": 100,          // <= 100
  "exclusiveMinimum": 0,   // > 0 (not equal)
  "multipleOf": 0.01       // Must be a multiple of 0.01 (2 decimals)
}

Array Validation

{
  "type": "array",
  "items": { "type": "string" },  // Each item must be a string
  "minItems": 1,                   // At least 1 item
  "maxItems": 10,                  // At most 10 items
  "uniqueItems": true              // No duplicates
}

Required Fields & Defaults

The required keyword lists which properties must be present:

{
  "type": "object",
  "properties": {
    "id": { "type": "integer" },
    "name": { "type": "string" },
    "role": { "type": "string", "default": "user" }
  },
  "required": ["id", "name"]
}

Here, id and name are mandatory. role is optional and defaults to "user" if omitted (though the default is advisory — your application code must apply it).

Nested Objects & References

Real-world data is nested. JSON Schema handles this naturally:

{
  "type": "object",
  "properties": {
    "user": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "address": { "$ref": "#/$defs/address" }
      },
      "required": ["name"]
    }
  },
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string", "pattern": "^[0-9]{5,6}$" }
      },
      "required": ["street", "city"]
    }
  }
}

The $ref keyword references a reusable definition from $defs, keeping your schema DRY.

Conditional Validation

JSON Schema supports if/then/else for conditional rules:

{
  "type": "object",
  "properties": {
    "payment_type": { "enum": ["card", "upi", "cash"] }
  },
  "if": {
    "properties": { "payment_type": { "const": "card" } }
  },
  "then": {
    "properties": {
      "card_number": { "type": "string", "pattern": "^[0-9]{16}$" }
    },
    "required": ["card_number"]
  }
}

If payment_type is "card", then card_number is required and must be 16 digits.

Composition Keywords

Combine multiple schemas with logical operators:

  • allOf — Must match ALL schemas (intersection)
  • anyOf — Must match at least ONE schema (union)
  • oneOf — Must match EXACTLY one schema (exclusive OR)
  • not — Must NOT match the schema
{
  "oneOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "integer", "minimum": 0 }
  ]
}
// Valid: "hello" or 42
// Invalid: "toolong" or -1

Popular Validation Libraries

LanguageLibraryDraft Support
JavaScriptajv2020-12, 2019-09, Draft-07
Pythonjsonschema2020-12, 2019-09, Draft-07
Javanetworknt/json-schema-validator2020-12, Draft-07
Gosanthosh-tekuri/jsonschema2020-12
PHPopis/json-schema2020-12

JavaScript example with Ajv

import Ajv from "ajv";
const ajv = new Ajv();

const schema = {
  type: "object",
  properties: {
    name: { type: "string" },
    age: { type: "integer", minimum: 0 }
  },
  required: ["name"],
  additionalProperties: false
};

const validate = ajv.compile(schema);
const valid = validate({ name: "Rahul", age: 28 });

if (!valid) console.log(validate.errors);

Best Practices

  • Use additionalProperties: false in strict APIs to reject unknown fields
  • Set "$schema" at the top to declare the draft version
  • Use $defs and $ref for reusable definitions — avoid copy-pasting schemas
  • Add "format" for strings like emails, dates, URIs (note: format validation is optional by default in most libraries)
  • Test edge cases: empty strings, null values, empty arrays, and boundary numbers
  • Version your schemas: include a version field or use separate schema files per API version

Quick Reference

type             → string, number, integer, boolean, array, object, null
enum             → exact allowed values: ["red","green","blue"]
const            → single exact value: "active"
pattern          → regex for strings: "^[a-z]+$"
minimum/maximum  → number bounds
minLength/maxLength → string length bounds
minItems/maxItems   → array size bounds
required         → list of mandatory property names
$ref             → reference another schema definition
if/then/else     → conditional validation
allOf/anyOf/oneOf   → schema composition

Working with JSON? Format and validate your data instantly.

Open JSON Formatter →