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
`json
{
"type": "string",
"minLength": 3,
"maxLength": 50,
"pattern": "^[a-z0-9_]+$",</p>
<p>"format": "email"</p>
<p>}</p>
</code>`<code>
</code>format<code> options: </code>email<code>, </code>uri<code>, </code>date<code> (YYYY-MM-DD), </code>date-time<code>, </code>ipv4<code>, </code>hostname<code>.
<h3>Number Validation</h3>
</code>`<code>json
<p>{</p>
<p>"type": "number",</p>
<p>"minimum": 0,</p>
<p>"maximum": 100,</p>
<p>"multipleOf": 0.01</p>
<p>}</p>
</code>`<code>
<p>Use </code>exclusiveMinimum<code>/</code>exclusiveMaximum<code> for strict bounds (in draft-07, these take numeric values).</p>
<h3>Array Validation</h3>
</code>`<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>`<code>
<p>When </code>items<code> is a schema, every element must match it. For tuple validation (different schema per position), set </code>items<code> to an array of schemas.</p>
<h3>Object Composition</h3>
</code>`<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>`<code>
</code>required<code> lists mandatory keys. </code>additionalProperties: false<code> rejects undeclared keys.
<h2>Schema Composition</h2>
<h3></code>allOf<code>, </code>anyOf<code>, </code>oneOf<code></h3>
<ul><li></code>allOf<code> — must be valid against every schema (AND)</li><li></code>anyOf<code> — must be valid against at least one schema (OR)</li><li></code>oneOf<code> — must be valid against exactly one schema</li></ul>
</code>`<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>`<code>
<p>This is a discriminated union: exactly one branch matches based on the </code>type<code> field.</p>
<h3>Reusable Definitions with </code>$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<code>, reference it anywhere with </code>$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<code>) in real time:</p>
<strong><a href="https://snappytools.app/json-schema-validator/">JSON Schema Validator — SnappyTools</a></strong>
<p>Entirely client-side — your data never leaves the browser. Safe for API payloads with credentials or private configuration.</p>
<h2>Validate in Code</h2>
<strong>JavaScript (Node.js):</strong>
</code>`<code>bash
<p>npm install ajv ajv-formats</p>
</code>`<code>
</code>`<code>js
<p>import Ajv from 'ajv';</p>
<p>import addFormats from 'ajv-formats';</p>
<p>const ajv = new Ajv();</p>
<p>addFormats(ajv);</p>
<p>const validate = ajv.compile(schema);</p>
<p>if (!validate(data)) {</p>
<p>console.error(validate.errors);</p>
<p>}</p>
</code>`<code>
<strong>Python:</strong>
</code>`<code>bash
<p>pip install jsonschema</p>
</code>`<code>
</code>`<code>python
<p>from jsonschema import validate, ValidationError</p>
<p>try:</p>
<p>validate(instance=data, schema=schema)</p>
<p>except ValidationError as e:</p>
<p>print(e.message, list(e.absolute_path))</p>
</code>`<code>
<p>Schemas written for the browser tool work identically in both libraries — same standard, no rewriting.</p>
<h2>Common Errors Explained</h2>
<strong>"Additional property not allowed"</strong> — your JSON has a field not listed in </code>properties<code>, and the schema has </code>additionalProperties: false<code>. Add the field to the schema or remove it from the JSON.
<strong>"Missing required property"</strong> — a field listed in </code>required<code> is absent from the JSON. Either add it or remove it from </code>required<code>.
<strong>"Expected type integer, got string"</strong> — the JSON field is a quoted string (</code>"42"<code>) where the schema expects a number (</code>42<code>). Remove the quotes.
<strong>"Value must match exactly one schema in oneOf (matched 0)"</strong> — the data doesn't match any branch. Check the discriminator field and the branch conditions.
<h2>Summary</h2>
<p>| Keyword | Purpose |</p>
<p>|---------|---------|</p>
<p>| </code>type<code> | Restrict to a JSON type |</p>
<p>| </code>properties<code> + </code>required<code> | Define expected object fields |</p>
<p>| </code>additionalProperties<code> | Control unknown fields |</p>
<p>| </code>enum<code> / </code>const<code> | Restrict to fixed values |</p>
<p>| </code>minLength<code> / </code>maxLength<code> | String length bounds |</p>
<p>| </code>minimum<code> / </code>maximum<code> | Numeric bounds |</p>
<p>| </code>items<code> | Array element schema |</p>
<p>| </code>allOf<code> / </code>anyOf<code> / </code>oneOf<code> | Schema composition |</p>
<p>| </code>$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/