All posts

Validating JSON Against a Schema: A Practical Guide to JSON Schema

July 27, 2026 · DevTools

json
json-schema
validation
api
developer-tools

Your API receives a payload. You expect { "userId": 42, "email": "alice@example.com", "role": "admin" }. Instead you receive { "user_id": "42", "email": "alice", "role": 0 }. The code does not crash — user_id is just ignored, email is an invalid address, and role is a number instead of a string. The bug surfaces three layers away from where it was introduced.

JSON Schema is the tool for catching this at the boundary. Instead of defensive coding inside your handler, you declare what valid input looks like and reject everything else at the door.

You can validate any JSON document against a schema with the JSON Schema Validator — it runs in your browser and reports errors with instance paths that point directly to the offending field.

What JSON Schema is

JSON Schema (draft-07 is the most widely deployed version; draft 2019-09 and 2020-12 are newer) is a vocabulary for describing the shape of a JSON document. A schema is itself a JSON document. It uses a $schema keyword to declare which draft it follows.

{
  "$schema": "https://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": ["userId", "email", "role"],
  "properties": {
    "userId": {
      "type": "integer",
      "minimum": 1
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "role": {
      "type": "string",
      "enum": ["admin", "user", "guest"]
    }
  }
}

This schema says: the document must be an object, it must have userId, email, and role, their types and constraints are as specified, and nothing else is allowed (additionalProperties: false is implied if not set).

The keywords that matter most

type — the most fundamental constraint. Valid values are: string, number, integer, boolean, array, object, null. You can also use ["string", "null"] for a nullable string.

required — an array of property names that must be present. Missing required fields are the most common validation error.

properties — declares each expected field and its constraints. Each property can have its own nested schema.

additionalProperties — controls what happens with fields not listed in properties. false rejects them. An object schema allows them and applies that schema to their values.

format — semantic validation for known formats. JSON Schema implementations vary in how strictly they enforce formats:

{
  "format": "email",
  "format": "uri",
  "format": "date-time",
  "format": "ipv4",
  "format": "uuid"
}

Not all validators enforce formats — some treat them as annotations only. For production, use a validator that enforces the formats you depend on, or add explicit pattern constraints for critical fields.

pattern — a regular expression the string must match. More portable than format across validators:

{ "type": "string", "pattern": "^[A-Z]{2}\\d{6}$" }

minimum, maximum, minLength, maxLength — numeric and string range constraints. Useful for API rate limits, pagination parameters, and string length.

Nested objects and arrays

{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["sku", "quantity"],
        "properties": {
          "sku": { "type": "string" },
          "quantity": { "type": "integer", "minimum": 1 }
        }
      },
      "minItems": 1
    }
  }
}

This schema requires items to be an array of objects, each with a string sku and an integer quantity of at least 1, and the array must have at least one item.

Error reporting at the right level

A validator returns errors like:

[
  {
    "instancePath": "/items/2/quantity",
    "schemaPath": "/properties/items/items/properties/quantity/minimum",
    "keyword": "minimum",
    "params": { "comparison": ">=", "limit": 1 },
    "message": "must be >= 1"
  }
]

/items/2/quantity points directly to the offending field (third array item, quantity property). This is precise enough to return a useful error to the API caller: "item 3: quantity must be at least 1".

The JSON Schema Validator shows these errors inline, with the normalized instance path and the specific constraint that failed.

OneOf and anyOf for discriminated unions

When a field determines the shape of the rest of the object:

{
  "oneOf": [
    {
      "properties": { "type": { "const": "credit-card" } },
      "required": ["type", "cardNumber"],
      "properties": { "cardNumber": { "type": "string" } }
    },
    {
      "properties": { "type": { "const": "bank-transfer" } },
      "required": ["type", "accountNumber"],
      "properties": { "accountNumber": { "type": "string" } }
    }
  ]
}

oneOf requires exactly one subschema to match. anyOf allows one or more. allOf requires all to match (useful for composing schemas).

Generating a schema from a JSON document

For a quick start, generate a schema from a representative JSON document and then tighten it. The JSON to TypeScript Converter can produce TypeScript interfaces from JSON, which gives you the type structure as a starting point.

Where validation belongs

Validate at the API boundary — the first point where untrusted input enters your system. Do not validate at every layer. If your database layer trusts your service layer, and your service layer trusts your API layer, validation at the API boundary is sufficient. Defensive validation at every layer leads to contradictory rules and maintenance headaches.

Use the JSON Schema Validator to check your payloads before deploying — or wire it into your API framework's middleware so invalid requests are rejected with a clear error before they reach your handler.