X Xerobit

JSON Schema Validation — Validate JSON Structure and Types

JSON Schema defines the structure and types your JSON must conform to. Here's how to write JSON Schema definitions, validate JSON in JavaScript and Python, and handle common...

Mian Ali Khalid · · 6 min read
Use the tool
JSON Formatter
Format, validate, and beautify JSON online. 100% client-side — your data never leaves your browser.
Open JSON Formatter →

JSON Schema is a vocabulary for describing the structure, types, and constraints of JSON data. Validating JSON against a schema catches malformed API payloads, configuration errors, and data quality issues before they cause runtime failures.

Use the JSON Formatter to validate and inspect JSON documents.

Basic JSON Schema structure

A JSON Schema is itself a JSON document with $schema, type, and constraint keywords:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "User",
  "description": "A user account",
  "type": "object",
  "required": ["id", "name", "email"],
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique user identifier",
      "minimum": 1
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "role": {
      "type": "string",
      "enum": ["admin", "user", "moderator"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "uniqueItems": true
    },
    "active": {
      "type": "boolean",
      "default": true
    }
  },
  "additionalProperties": false
}

Valid JSON:

{ "id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin" }

Invalid (missing required email):

{ "id": 1, "name": "Alice" }

Invalid (extra property not in schema):

{ "id": 1, "name": "Alice", "email": "alice@example.com", "unknown_field": true }

Type keywords

{ "type": "string" }
{ "type": "number" }    // Includes integers and floats
{ "type": "integer" }   // Whole numbers only
{ "type": "boolean" }
{ "type": "null" }
{ "type": "array" }
{ "type": "object" }
{ "type": ["string", "null"] }  // Multiple allowed types

String constraints

{
  "type": "string",
  "minLength": 1,
  "maxLength": 255,
  "pattern": "^[a-zA-Z0-9_-]+$",  // Regex pattern
  "format": "email"                // Semantic format
}

Format keywords:

  • "email" — valid email address
  • "date" — YYYY-MM-DD
  • "date-time" — ISO 8601 datetime
  • "uri" — valid URI
  • "uuid" — UUID format
  • "ipv4" — IPv4 address
  • "ipv6" — IPv6 address

Note: Format validation is optional in most validators (opt-in).

Number constraints

{
  "type": "number",
  "minimum": 0,
  "maximum": 100,
  "exclusiveMinimum": 0,  // > 0, not >= 0
  "exclusiveMaximum": 100, // < 100, not <= 100
  "multipleOf": 5          // Must be divisible by 5
}

Array constraints

{
  "type": "array",
  "items": { "type": "string" },  // All items must be strings
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true  // No duplicates
}

// Tuple validation (different schema per position):
{
  "type": "array",
  "prefixItems": [
    { "type": "number" },  // First item: number
    { "type": "string" },  // Second item: string
    { "type": "boolean" }  // Third item: boolean
  ],
  "items": false  // No additional items allowed
}

Conditional schemas

{
  "type": "object",
  "properties": {
    "type": { "type": "string", "enum": ["personal", "business"] },
    "company_name": { "type": "string" }
  },
  "if": {
    "properties": { "type": { "const": "business" } }
  },
  "then": {
    "required": ["company_name"]  // Required if type = "business"
  },
  "else": {
    "properties": { "company_name": false }  // Not allowed if type = "personal"
  }
}

Reusable schemas with $ref

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$defs": {
    "address": {
      "type": "object",
      "required": ["street", "city"],
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string", "pattern": "^[0-9]{5}$" }
      }
    }
  },
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "billing_address": { "$ref": "#/$defs/address" },
    "shipping_address": { "$ref": "#/$defs/address" }
  }
}

Validation in JavaScript (ajv)

npm install ajv
import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv({ allErrors: true }); // Report all errors, not just first
addFormats(ajv); // Add format validation (email, date, uri, etc.)

const schema = {
  type: 'object',
  required: ['name', 'email'],
  properties: {
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 0 },
  },
  additionalProperties: false,
};

const validate = ajv.compile(schema);

// Valid data:
const validData = { name: 'Alice', email: 'alice@example.com', age: 30 };
if (!validate(validData)) {
  console.error(validate.errors);
} else {
  console.log('Valid!');
}

// Invalid data:
const invalidData = { name: '', email: 'not-an-email', extra: 'field' };
validate(invalidData);
console.log(validate.errors);
/*
[
  { instancePath: '/name', message: 'must NOT have fewer than 1 characters' },
  { instancePath: '/email', message: 'must match format "email"' },
  { instancePath: '', message: 'must NOT have additional properties' }
]
*/

Validation in Python (jsonschema)

pip install jsonschema
from jsonschema import validate, ValidationError, Draft7Validator

schema = {
    "type": "object",
    "required": ["name", "email"],
    "properties": {
        "name": {"type": "string", "minLength": 1},
        "email": {"type": "string", "format": "email"},
        "age": {"type": "integer", "minimum": 0},
    },
    "additionalProperties": False,
}

# Simple validation (raises on first error):
try:
    validate(instance={"name": "Alice", "email": "alice@example.com"}, schema=schema)
    print("Valid!")
except ValidationError as e:
    print(f"Error: {e.message}")

# All errors (Draft7Validator):
validator = Draft7Validator(schema)
data = {"name": "", "email": "not-email", "extra": True}

errors = list(validator.iter_errors(data))
for error in errors:
    print(f"{error.json_path}: {error.message}")

API input validation pattern

// Express.js middleware using JSON Schema:
import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);

function validateBody(schema) {
  const validate = ajv.compile(schema);
  return (req, res, next) => {
    if (!validate(req.body)) {
      return res.status(400).json({
        error: 'Validation failed',
        details: validate.errors.map(e => ({
          field: e.instancePath || 'body',
          message: e.message,
        })),
      });
    }
    next();
  };
}

const createUserSchema = {
  type: 'object',
  required: ['name', 'email'],
  properties: {
    name: { type: 'string', minLength: 1, maxLength: 100 },
    email: { type: 'string', format: 'email' },
  },
  additionalProperties: false,
};

app.post('/users', validateBody(createUserSchema), (req, res) => {
  // req.body is guaranteed to match the schema here
});

Related posts

Related tool

JSON Formatter

Format, validate, and beautify JSON online. 100% client-side — your data never leaves your browser.

Written by Mian Ali Khalid. Part of the Data & Format pillar.