JSON Schema Tutorial: Validate JSON with Examples

JSON Schema lets you describe the structure of a JSON document and validate data against it. It's used for API contract testing, form validation, configuration file validation, and auto-generating TypeScript types. This tutorial covers the essentials with real examples.

What is JSON Schema?

JSON Schema is a vocabulary for annotating and validating JSON data. A schema is itself a JSON document that describes what valid data looks like: which fields are required, what types they must be, and what constraints they must satisfy.

// A simple schema for a user object
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["id", "email"],
  "properties": {
    "id": { "type": "integer" },
    "email": { "type": "string", "format": "email" },
    "name": { "type": "string" }
  }
}

// Valid data:
{ "id": 1, "email": "alice@example.com", "name": "Alice" }
{ "id": 2, "email": "bob@example.com" }

// Invalid data:
{ "email": "alice@example.com" }        // missing required "id"
{ "id": "one", "email": "a@b.com" }    // id must be integer

Core keywords

type

Specifies the expected JSON type. Valid values: string, number, integer, boolean, array, object, null.

{ "type": "string" }    // validates: "hello"
{ "type": "integer" }   // validates: 42, not 3.14
{ "type": "number" }    // validates: 42, 3.14
{ "type": ["string", "null"] }  // validates: "hello" or null

properties and required

properties defines a schema for each key. required lists keys that must be present. A key in properties but not in requiredis optional.

{
  "type": "object",
  "required": ["title", "price"],
  "properties": {
    "title": { "type": "string" },
    "price": { "type": "number", "minimum": 0 },
    "description": { "type": "string" }   // optional
  }
}

String constraints

{
  "type": "string",
  "minLength": 1,        // at least 1 character
  "maxLength": 100,      // at most 100 characters
  "pattern": "^[a-z]+$" // must match regex (lowercase letters only)
}

Number constraints

{
  "type": "number",
  "minimum": 0,          // >= 0
  "maximum": 100,        // <= 100
  "exclusiveMinimum": 0, // > 0 (draft-07 style)
  "multipleOf": 0.5      // must be a multiple of 0.5
}

enum

Restricts a value to a fixed set of allowed values.

{ "enum": ["pending", "active", "suspended", "deleted"] }

// Also works for mixed types:
{ "enum": [1, "one", null, true] }

format

Provides semantic validation hints. Validators may or may not enforce format checks — it depends on the implementation.

{ "type": "string", "format": "email" }
{ "type": "string", "format": "date" }        // "2024-01-15"
{ "type": "string", "format": "date-time" }   // "2024-01-15T10:30:00Z"
{ "type": "string", "format": "uri" }
{ "type": "string", "format": "uuid" }

Array validation

{
  "type": "array",
  "items": { "type": "string" },   // all items must be strings
  "minItems": 1,                   // at least one item
  "maxItems": 10,                  // at most ten items
  "uniqueItems": true              // no duplicate values
}

Composition keywords

allOf, anyOf, oneOf

// allOf — must satisfy ALL sub-schemas
{
  "allOf": [
    { "type": "object" },
    { "required": ["name"] }
  ]
}

// anyOf — must satisfy AT LEAST ONE sub-schema
{
  "anyOf": [
    { "type": "string" },
    { "type": "number" }
  ]
}

// oneOf — must satisfy EXACTLY ONE sub-schema
{
  "oneOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "string", "minLength": 10 }
  ]
}

not

// Must NOT be a string
{ "not": { "type": "string" } }

$ref and $defs

Use $defs to define reusable sub-schemas and reference them with $ref:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "billing": { "$ref": "#/$defs/address" },
    "shipping": { "$ref": "#/$defs/address" }
  },
  "$defs": {
    "address": {
      "type": "object",
      "required": ["street", "city"],
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string" }
      }
    }
  }
}

Complete annotated example

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Product",
  "type": "object",
  "required": ["id", "name", "price", "category"],
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "price": {
      "type": "number",
      "minimum": 0,
      "exclusiveMinimum": 0
    },
    "category": {
      "type": "string",
      "enum": ["electronics", "books", "clothing", "food"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 },
      "uniqueItems": true
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "metadata": {
      "type": "object",
      "additionalProperties": { "type": "string" }
    }
  }
}

Draft versions

JSON Schema has multiple published drafts: draft-04, draft-06, draft-07, draft 2019-09, draft 2020-12. The most widely supported in libraries and tooling is draft-07. When writing schemas, always declare the version with $schema so validators know which spec to use.

Validate JSON against a schema

Paste your JSON and schema into Jsonic's JSON Schema Validator to check for errors instantly. Runs in your browser — nothing is uploaded.

Open JSON Schema Validator