JSON Schema Primer: Validate API Payloads Before They Break Your App

posted 5 min read

The Problem JSON Schema Solves

APIs receive bad data. A field expected to be an integer arrives as a string. A required field is missing. An email field contains "not-an-email". Without validation, these problems surface deep inside your application logic — often as cryptic runtime errors, not clear validation messages.

JSON Schema is the industry-standard solution: a declarative language for describing exactly what valid JSON should look like. Write the schema once, validate anywhere.

What Is a JSON Schema?

A JSON Schema is a JSON document that describes the structure and constraints of another JSON document.

``json

{

"type": "object",

"required": ["id", "email"],

"properties": {

"id": { "type": "integer", "minimum": 1 },

"email": { "type": "string", "format": "email" }

},

"additionalProperties": false

}

`

This schema accepts objects with an integer id (at least 1) and a valid email string. Any extra fields are rejected.

The Draft Versions

JSON Schema has several published drafts. Draft-07 is the most important to know:

  • Used by OpenAPI 3.0 (the Swagger standard for REST APIs)
  • Supported by Ajv (JavaScript), jsonschema (Python), and most tooling
  • Added if/then/else conditional validation
  • Still the most widely deployed version in production

Draft 2020-12 is newer but draft-07 is what you'll encounter in existing codebases and tooling.

Core Keywords

Types

<code>json <p>{ "type": "string" }</p> <p>{ "type": ["string", "null"] }</p> </code>

Types: string, number, integer, boolean, null, array, object. An array of types means "any of these".

String Validation

<code>json <p>{</p> <p>"type": "string",</p> <p>"minLength": 3,</p> <p>"maxLength": 50,</p> <p>"pattern": "^[a-z0-9_]+$",</p> <p>"format": "email"</p> <p>}</p> </code>

format options: email, uri, date (YYYY-MM-DD), date-time, ipv4, hostname.

Number Validation

<code>json <p>{</p> <p>"type": "number",</p> <p>"minimum": 0,</p> <p>"maximum": 100,</p> <p>"multipleOf": 0.01</p> <p>}</p> </code>

Use exclusiveMinimum/exclusiveMaximum for strict bounds (in draft-07, these take numeric values).

Array Validation

<code>json <p>{</p> <p>"type": "array",</p> <p>"items": { "type": "string" },</p> <p>"minItems": 1,</p> <p>"maxItems": 20,</p> <p>"uniqueItems": true</p> <p>}</p> </code>

When items is a schema, every element must match it. For tuple validation (different schema per position), set items to an array of schemas.

Object Composition

<code>json <p>{</p> <p>"type": "object",</p> <p>"required": ["name"],</p> <p>"properties": {</p> <p>"name": { "type": "string" },</p> <p>"role": { "type": "string", "enum": ["admin", "user"] }</p> <p>},</p> <p>"additionalProperties": false</p> <p>}</p> </code>

required lists mandatory keys. additionalProperties: false rejects undeclared keys.

Schema Composition

allOf, anyOf, oneOf

  • allOf — must be valid against every schema (AND)
  • anyOf — must be valid against at least one schema (OR)
  • oneOf — must be valid against exactly one schema

<code>json <p>{</p> <p>"oneOf": [</p> <p>{ "properties": { "type": { "const": "circle" } }, "required": ["radius"] },</p> <p>{ "properties": { "type": { "const": "rect" } }, "required": ["width", "height"] }</p> <p>]</p> <p>}</p> </code>

This is a discriminated union: exactly one branch matches based on the type field.

Reusable Definitions with $ref

<code>json <p>{</p> <p>"$defs": {</p> <p>"Address": {</p> <p>"type": "object",</p> <p>"required": ["street", "city"],</p> <p>"properties": {</p> <p>"street": { "type": "string" },</p> <p>"city": { "type": "string" },</p> <p>"zip": { "type": "string", "pattern": "^\\d{5}$" }</p> <p>}</p> <p>}</p> <p>},</p> <p>"type": "object",</p> <p>"properties": {</p> <p>"billing": { "$ref": "#/$defs/Address" },</p> <p>"shipping": { "$ref": "#/$defs/Address" }</p> <p>}</p> <p>}</p> </code>

Define a schema once in $defs, reference it anywhere with $ref. Eliminates duplication.

Conditional Validation with if/then/else

<code>json <p>{</p> <p>"if": { "properties": { "plan": { "const": "paid" } }, "required": ["plan"] },</p> <p>"then": { "required": ["paymentMethod"] },</p> <p>"else": {}</p> <p>}</p> </code>

If plan equals "paid", then paymentMethod is required. If plan is anything else, no additional constraint applies.

Validate in Your Browser

You can test JSON Schema immediately — no installation needed. Paste your schema on the left, paste your JSON on the right, and see clear path-based error messages (e.g. $.user.address.zip) in real time:

JSON Schema Validator — SnappyTools

Entirely client-side — your data never leaves the browser. Safe for API payloads with credentials or private configuration.

Validate in Code

JavaScript (Node.js):

<code>bash <p>npm install ajv ajv-formats</p> </code>

`js
import Ajv from 'ajv';


import addFormats from 'ajv-formats';

const ajv = new Ajv();

addFormats(ajv);

const validate = ajv.compile(schema);

if (!validate(data)) {

console.error(validate.errors);

}

`

Python:

<code>bash <p>pip install jsonschema</p> </code>

`python
from jsonschema import validate, ValidationError

try:

validate(instance=data, schema=schema)

except ValidationError as e:

print(e.message, list(e.absolute_path))

`

Schemas written for the browser tool work identically in both libraries — same standard, no rewriting.

Common Errors Explained

"Additional property not allowed" — your JSON has a field not listed in properties, and the schema has additionalProperties: false. Add the field to the schema or remove it from the JSON.

"Missing required property" — a field listed in required is absent from the JSON. Either add it or remove it from required.

"Expected type integer, got string" — the JSON field is a quoted string ("42") where the schema expects a number (42). Remove the quotes.

"Value must match exactly one schema in oneOf (matched 0)" — the data doesn't match any branch. Check the discriminator field and the branch conditions.

Summary

| Keyword | Purpose |

|---------|---------|

| type | Restrict to a JSON type |

| properties + required | Define expected object fields |

| additionalProperties | Control unknown fields |

| enum / const | Restrict to fixed values |

| minLength / maxLength | String length bounds |

| minimum / maximum | Numeric bounds |

| items | Array element schema |

| allOf / anyOf / oneOf | Schema composition |

| $ref + $defs | Reusable definitions |

| if / then / else` | Conditional validation |

JSON Schema is the cheapest way to catch data problems before they reach your application logic. Write it once, validate everywhere.

Originally published at https://snappytools.app/json-schema-validator/

More Posts

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelski - Mar 19

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolioverified - Apr 1
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

5 comments
4 comments
3 comments

Contribute meaningful comments to climb the leaderboard and earn badges!