Home/Blog/JSON Schema Validation: A Complete Tutorial
Back to blog
JSON Tools10 min read

JSON Schema Validation: A Complete Tutorial

A complete guide to JSON Schema — writing schemas, validating data, using $ref and composition keywords, and integrating validation into your API pipeline.

AC

Alex Chen

Senior Software Engineer

#json#schema#validation#api

What Is JSON Schema?

JSON Schema is a vocabulary for annotating and validating JSON documents. It describes the structure of a JSON document — the types of fields, required vs optional fields, constraints on values, and relationships between fields. JSON Schema is the standard way to document API contracts, validate incoming data, and auto-generate forms, documentation, and TypeScript types.

A Simple JSON Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://api.example.com/schemas/user.json",
  "title": "User",
  "description": "A registered user in the system",
  "type": "object",
  "required": ["id", "email", "name"],
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique user identifier"
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "role": {
      "type": "string",
      "enum": ["admin", "user", "viewer"],
      "default": "user"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    }
  },
  "additionalProperties": false
}

Type Keywords

  • "type": "string" — string values
  • "type": "number" — any number (integer or float)
  • "type": "integer" — whole numbers only
  • "type": "boolean" — true or false
  • "type": "null" — the JSON null value
  • "type": "array" — JSON arrays
  • "type": "object" — JSON objects
  • "type": ["string", "null"] — union of types (nullable string)

Composition Keywords

{
  "allOf": [
    { "$ref": "#/$defs/BaseUser" },
    { "required": ["admin_level"] }
  ],
  "anyOf": [
    { "type": "string" },
    { "type": "integer" }
  ],
  "oneOf": [
    { "required": ["card_number"] },
    { "required": ["bank_account"] }
  ],
  "not": {
    "properties": { "status": { "enum": ["deleted"] } }
  }
}

Using $ref for Reusable Components

{
  "$defs": {
    "Address": {
      "type": "object",
      "required": ["street", "city"],
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string", "pattern": "^[0-9]{5}$" }
      }
    }
  },
  "properties": {
    "billing": { "$ref": "#/$defs/Address" },
    "shipping": { "$ref": "#/$defs/Address" }
  }
}

Validating Data with ajv (Node.js)

import Ajv from 'ajv'
import addFormats from 'ajv-formats'

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

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

const validate = ajv.compile(schema)
const data = { email: 'not-an-email', name: '' }

if (!validate(data)) {
  console.log(validate.errors)
  // [
  //   { keyword: 'format', dataPath: '.email', message: 'must match format "email"' },
  //   { keyword: 'minLength', dataPath: '.name', message: 'must NOT be shorter than 1 characters' }
  // ]
}

Schema Comparison with DiffChecker Pro

When evolving a JSON Schema, comparing the old and new schema versions ensures you understand the impact of every change:

  • Removing a required field = backward-compatible (for producers), breaking (for consumers that depend on it)
  • Adding a required field = breaking change for existing producers
  • Narrowing constraints (reducing maxLength) = potentially breaking for existing data
  • Adding optional properties = always safe

Paste your old and new schema JSON into DiffChecker Pro's JSON diff mode. The structural diff highlights exactly which constraints changed, which properties were added or removed, and which required fields changed — giving you a clear impact analysis for every schema version bump.

Generating TypeScript Types from JSON Schema

npx json-schema-to-typescript user.schema.json > user.types.ts

# Result:
export interface User {
  id: number
  email: string
  name: string
  role?: 'admin' | 'user' | 'viewer'
  age?: number
}

Share this article

Was this article helpful?

Ready to try it? Start a free comparison →

AC

Alex Chen

Senior Software Engineer

Alex Chen writes about developer tools, software engineering best practices, and productivity for the DiffChecker Pro blog. With extensive experience in software development, Alex focuses on practical guides that help developers work more effectively.

Related Articles