DEV Community

Snappy Tools
Snappy Tools

Posted on

JSON Schema Validation: A Practical Guide with Examples

JSON Schema lets you define the exact structure and types of a JSON document and validate data against that definition. It's essential for API contracts, configuration validation, and any system that processes external JSON data.

What JSON Schema does

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["name", "email"],
  "properties": {
    "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", "guest"]
    }
  },
  "additionalProperties": false
}
Enter fullscreen mode Exit fullscreen mode

This schema says:

  • The document must be an object
  • name and email are required
  • name must be a string between 1–100 characters
  • email must match the email format
  • age, if present, must be an integer 0–150
  • role, if present, must be one of three values
  • No other properties are allowed

A JSON Schema validator lets you test JSON against a schema and see exactly which validation rules fail.

Core keywords

Type constraints:

{"type": "string"}
{"type": "number"}
{"type": "integer"}
{"type": "boolean"}
{"type": "null"}
{"type": "array"}
{"type": "object"}
{"type": ["string", "null"]}  // Multiple allowed types
Enter fullscreen mode Exit fullscreen mode

String constraints:

{
  "type": "string",
  "minLength": 5,
  "maxLength": 100,
  "pattern": "^[a-zA-Z0-9]+$"  // Regex pattern
}
Enter fullscreen mode Exit fullscreen mode

Number constraints:

{
  "type": "number",
  "minimum": 0,
  "maximum": 100,
  "exclusiveMinimum": 0,    // > 0 (exclusive)
  "exclusiveMaximum": 100,  // < 100 (exclusive)
  "multipleOf": 5           // Must be divisible by 5
}
Enter fullscreen mode Exit fullscreen mode

Array constraints:

{
  "type": "array",
  "items": {"type": "string"},  // Each item must be a string
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true           // No duplicate values
}
Enter fullscreen mode Exit fullscreen mode

Object constraints:

{
  "type": "object",
  "properties": {
    "name": {"type": "string"},
    "age": {"type": "integer"}
  },
  "required": ["name"],
  "additionalProperties": false,    // Reject unknown keys
  "minProperties": 1,
  "maxProperties": 5
}
Enter fullscreen mode Exit fullscreen mode

Combining schemas

allOf: Must match ALL of the subschemas:

{
  "allOf": [
    {"type": "object"},
    {"required": ["name"]},
    {"properties": {"name": {"minLength": 1}}}
  ]
}
Enter fullscreen mode Exit fullscreen mode

anyOf: Must match AT LEAST ONE subschema:

{
  "anyOf": [
    {"type": "string"},
    {"type": "number"}
  ]
}
Enter fullscreen mode Exit fullscreen mode

oneOf: Must match EXACTLY ONE subschema:

{
  "oneOf": [
    {"properties": {"type": {"const": "circle"}}, "required": ["radius"]},
    {"properties": {"type": {"const": "square"}}, "required": ["side"]}
  ]
}
Enter fullscreen mode Exit fullscreen mode

if/then/else (draft-07+): Conditional validation:

{
  "if": {
    "properties": {"type": {"const": "business"}}
  },
  "then": {
    "required": ["companyName", "vatNumber"]
  },
  "else": {
    "required": ["firstName", "lastName"]
  }
}
Enter fullscreen mode Exit fullscreen mode

$ref and $defs: Reusable schemas

{
  "$defs": {
    "Address": {
      "type": "object",
      "properties": {
        "street": {"type": "string"},
        "city": {"type": "string"},
        "country": {"type": "string"}
      },
      "required": ["street", "city", "country"]
    }
  },
  "type": "object",
  "properties": {
    "billingAddress": {"$ref": "#/$defs/Address"},
    "shippingAddress": {"$ref": "#/$defs/Address"}
  }
}
Enter fullscreen mode Exit fullscreen mode

$defs (formerly definitions in draft-07) stores reusable schema fragments. $ref references them.

Validating with ajv (JavaScript)

npm install ajv
Enter fullscreen mode Exit fullscreen mode
import Ajv from 'ajv';
import addFormats from 'ajv-formats';  // For "email", "uri", etc.

const ajv = new Ajv();
addFormats(ajv);

const schema = {
  type: 'object',
  required: ['name', 'email'],
  properties: {
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' }
  }
};

const validate = ajv.compile(schema);

const data = { name: 'Alice', email: 'alice@example.com' };
if (validate(data)) {
  console.log('Valid!');
} else {
  console.log('Errors:', validate.errors);
}
Enter fullscreen mode Exit fullscreen mode

Using ajv in Express.js for request validation:

app.post('/users', (req, res) => {
  if (!validate(req.body)) {
    return res.status(400).json({
      errors: validate.errors.map(e => ({
        field: e.instancePath,
        message: e.message
      }))
    });
  }
  // Data is valid, proceed
});
Enter fullscreen mode Exit fullscreen mode

Validating with jsonschema (Python)

pip install jsonschema
Enter fullscreen mode Exit fullscreen mode
from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "required": ["name", "email"],
    "properties": {
        "name": {"type": "string", "minLength": 1},
        "email": {"type": "string", "format": "email"}
    }
}

try:
    validate(instance={"name": "Alice", "email": "alice@example.com"}, schema=schema)
    print("Valid!")
except ValidationError as e:
    print(f"Invalid: {e.message}")
    print(f"At path: {list(e.path)}")
Enter fullscreen mode Exit fullscreen mode

Generating schemas from data

When you have sample data and need a schema:

Python:

from genson import SchemaBuilder

builder = SchemaBuilder()
builder.add_object({"name": "Alice", "age": 30, "active": True})
builder.add_object({"name": "Bob", "age": 25, "active": False, "tags": ["admin"]})
print(builder.to_json(indent=2))
Enter fullscreen mode Exit fullscreen mode

Node.js: Use quicktype.io, which generates JSON Schema, TypeScript interfaces, and other types from JSON samples.

Generated schemas are starting points — add required, minLength, enum, and other constraints based on your business rules.

JSON Schema drafts

Draft Year Key addition
Draft-04 2013 $schema, $ref, allOf/anyOf/oneOf
Draft-07 2019 if/then/else, $comment, readOnly
2019-09 2019 $defs (replaces definitions), $anchor
2020-12 2020 prefixItems, $dynamicRef, unevaluatedProperties

ajv supports all drafts. Most tools use draft-07 — it's widely supported and has the features needed for most use cases.


JSON Schema validation is a cleanly defined, well-supported standard for data contracts. Using it at your API boundaries reduces bugs, improves error messages, and makes data expectations explicit in machine-readable form.

Top comments (0)