DEV Community

arenasbob2024-cell
arenasbob2024-cell

Posted on • Originally published at viadreams.cc

JSON Schema Validation: The Definitive Guide

What Is JSON Schema?

JSON Schema is a declarative language for defining the structure, content, and constraints of JSON data. Think of it as a blueprint that describes what valid JSON looks like for your application. It is used for API request/response validation, form generation, configuration file validation, and documentation.

Your First JSON Schema

Suppose you have a user object:

{
  "name": "Alice",
  "email": "alice@example.com",
  "age": 30
}
Enter fullscreen mode Exit fullscreen mode

Here is a JSON Schema that validates this structure:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "User",
  "description": "A registered user in the system",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}
Enter fullscreen mode Exit fullscreen mode

Core Schema Types

JSON Schema supports these primitive types:

Type Description Example
string Text values "hello"
number Any numeric value 3.14
integer Whole numbers only 42
boolean True or false true
null Null value null
array Ordered list [1, 2, 3]
object Key-value pairs {"key": "value"}

String Validation

{
  "type": "string",
  "minLength": 1,
  "maxLength": 255,
  "pattern": "^[A-Z][a-z]+$",
  "format": "email"
}
Enter fullscreen mode Exit fullscreen mode

Common formats: email, uri, date, date-time, ipv4, ipv6, uuid, hostname.

Array Validation

{
  "type": "array",
  "items": {
    "type": "string"
  },
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true
}
Enter fullscreen mode Exit fullscreen mode

For tuple validation (fixed-length arrays with typed positions):

{
  "type": "array",
  "prefixItems": [
    { "type": "number" },
    { "type": "string" },
    { "type": "boolean" }
  ],
  "items": false
}
Enter fullscreen mode Exit fullscreen mode

This matches [42, "hello", true] but rejects [42, "hello", true, "extra"].

Composition Keywords

oneOf, anyOf, allOf

{
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "type": { "const": "email" },
        "address": { "type": "string", "format": "email" }
      },
      "required": ["type", "address"]
    },
    {
      "type": "object",
      "properties": {
        "type": { "const": "phone" },
        "number": { "type": "string", "pattern": "^[+]?[0-9]{10,15}$" }
      },
      "required": ["type", "number"]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

This validates either an email contact or a phone contact, but not both.

Reusable Schemas with $ref

The $ref keyword lets you reference and reuse schema definitions:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Order",
  "type": "object",
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string", "pattern": "^[0-9]{5}$" }
      },
      "required": ["street", "city", "zip"]
    },
    "product": {
      "type": "object",
      "properties": {
        "id": { "type": "integer" },
        "name": { "type": "string" },
        "price": { "type": "number", "minimum": 0 }
      },
      "required": ["id", "name", "price"]
    }
  },
  "properties": {
    "shipping_address": { "$ref": "#/$defs/address" },
    "billing_address": { "$ref": "#/$defs/address" },
    "items": {
      "type": "array",
      "items": { "$ref": "#/$defs/product" },
      "minItems": 1
    },
    "total": { "type": "number", "minimum": 0 }
  },
  "required": ["shipping_address", "items", "total"]
}
Enter fullscreen mode Exit fullscreen mode

Validation in Node.js with Ajv

Ajv is the most popular JSON Schema validator for JavaScript:

npm install ajv ajv-formats
Enter fullscreen mode Exit fullscreen mode
const Ajv = require('ajv');
const addFormats = require('ajv-formats');

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

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

const validate = ajv.compile(schema);

// Valid data
const validUser = { name: 'Alice', email: 'alice@example.com', age: 30 };
console.log(validate(validUser)); // true

// Invalid data
const invalidUser = { name: '', email: 'not-an-email', age: -5 };
console.log(validate(invalidUser)); // false
console.log(validate.errors);
// [
//   { keyword: 'minLength', instancePath: '/name', ... },
//   { keyword: 'format', instancePath: '/email', ... },
//   { keyword: 'minimum', instancePath: '/age', ... }
// ]
Enter fullscreen mode Exit fullscreen mode

Express Middleware Example

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(err => ({
          field: err.instancePath,
          message: err.message
        }))
      });
    }
    next();
  };
}

app.post('/api/users', validateBody(userSchema), (req, res) => {
  // req.body is guaranteed to be valid here
  res.json({ message: 'User created', user: req.body });
});
Enter fullscreen mode Exit fullscreen mode

Validation in Python with jsonschema

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

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string", "minLength": 1},
        "email": {"type": "string", "format": "email"},
        "scores": {
            "type": "array",
            "items": {"type": "number", "minimum": 0, "maximum": 100}
        }
    },
    "required": ["name", "email"],
    "additionalProperties": False
}

# Valid data
valid_data = {"name": "Alice", "email": "alice@example.com", "scores": [95, 87, 92]}
validate(instance=valid_data, schema=schema)
print("Validation passed!")

# Invalid data
try:
    invalid_data = {"name": "", "email": "bad", "scores": [101]}
    validate(instance=invalid_data, schema=schema)
except ValidationError as e:
    print(f"Validation error: {e.message}")
    print(f"Path: {list(e.absolute_path)}")
Enter fullscreen mode Exit fullscreen mode

Conditional Schemas

Use if/then/else for conditional validation:

{
  "type": "object",
  "properties": {
    "payment_method": { "enum": ["credit_card", "bank_transfer"] },
    "card_number": { "type": "string" },
    "routing_number": { "type": "string" }
  },
  "if": {
    "properties": { "payment_method": { "const": "credit_card" } }
  },
  "then": {
    "required": ["card_number"]
  },
  "else": {
    "required": ["routing_number"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Generate Schemas Instantly

Writing JSON Schemas by hand can be tedious. Use the DevToolBox JSON Schema Generator to paste any JSON sample and automatically generate a complete JSON Schema with proper types, required fields, and validation constraints.

Top comments (0)