JSON Schema Validator Guide: Contract-First API Development
Technical Mastery Overview
Why JSON Schema Validation Is Different from Formatting
Formatting a JSON document — adding indentation, checking for syntax errors — validates that the text is parseable. It tells you nothing about whether the parsed data is correct.
These are two separate problems:
| Check | What it catches |
|---|---|
| Syntax validation (formatter) | Missing commas, unquoted keys, mismatched brackets |
| Schema validation | Wrong types, missing required fields, out-of-range values, invalid enum values |
A payload can be perfectly formatted and still be semantically wrong:
{
"userId": null,
"amount": "49.99",
"status": "pending_approval"
}
This is valid JSON. But if your schema says userId is required and non-null, amount must be a number, and status must be one of ["pending", "complete", "failed"] — this payload fails validation on all three counts. Schema validation catches these before the data reaches your application code.
JSON Schema Basics
A JSON Schema is itself a JSON document that describes the shape of valid data:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["id", "email", "role"],
"properties": {
"id": {
"type": "string",
"format": "uuid",
"description": "User's unique identifier"
},
"email": {
"type": "string",
"format": "email",
"maxLength": 254
},
"role": {
"type": "string",
"enum": ["admin", "editor", "viewer"]
},
"age": {
"type": "integer",
"minimum": 18,
"maximum": 120
},
"tags": {
"type": "array",
"items": { "type": "string" },
"uniqueItems": true
}
},
"additionalProperties": false
}
This schema enforces: id, email, and role are required; age if present must be 18–120; tags must be an array of unique strings; no extra properties are allowed.
Type Keywords
{ "type": "string" } // "hello", "123", ""
{ "type": "number" } // 42, 3.14, -7
{ "type": "integer" } // 42, -7 (not 3.14)
{ "type": "boolean" } // true, false
{ "type": "null" } // null
{ "type": "array" } // [], [1, 2, 3]
{ "type": "object" } // {}, {"key": "value"}
// Multiple types (nullable field)
{ "type": ["string", "null"] }
String Constraints
{
"type": "string",
"minLength": 1,
"maxLength": 100,
"pattern": "^[a-zA-Z0-9_-]+$",
"format": "email"
}
Built-in format keywords (enforcement depends on the validator):
"email"— valid email address"date"—YYYY-MM-DD"date-time"— ISO 8601 datetime"uri"— valid URI"uuid"— UUID v4 format"ipv4"/"ipv6"— IP address
For pattern validation, test your regex with our Regex Tester before embedding it in a schema — a broken pattern silently passes everything.
Number Constraints
{
"type": "number",
"minimum": 0,
"maximum": 1000000,
"exclusiveMinimum": 0,
"multipleOf": 0.01
}
exclusiveMinimum: 0 means the value must be > 0 (not >= 0). multipleOf: 0.01 enforces exactly 2 decimal places — useful for currency amounts.
Nested Objects and $ref
For complex schemas, define reusable components with $ref:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"address": {
"type": "object",
"required": ["street", "city", "country"],
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
}
}
},
"type": "object",
"properties": {
"billing": { "$ref": "#/definitions/address" },
"shipping": { "$ref": "#/definitions/address" }
}
}
This defines address once and references it for both billing and shipping — keeping the schema DRY and maintainable.
Array Validation
{
"type": "array",
"minItems": 1,
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "object",
"required": ["id", "quantity"],
"properties": {
"id": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 }
}
}
}
uniqueItems: true enforces that no two array elements are identical — useful for tag lists, permission arrays, and ID collections.
Conditional Validation (if/then/else)
JSON Schema Draft 7+ supports conditional validation:
{
"if": {
"properties": { "type": { "const": "business" } }
},
"then": {
"required": ["companyName", "taxId"]
},
"else": {
"required": ["firstName", "lastName"]
}
}
If type is "business", then companyName and taxId are required. Otherwise, firstName and lastName are required. This avoids having to define separate schemas for each entity type.
Schema Versions
| Version | $schema URI |
Notes |
|---|---|---|
| Draft 7 | http://json-schema.org/draft-07/schema# |
Most widely supported |
| Draft 2019-09 | https://json-schema.org/draft/2019-09/schema |
Added unevaluatedProperties |
| Draft 2020-12 | https://json-schema.org/draft/2020-12/schema |
Latest, best conditional support |
Our validator supports all major drafts. Always include the $schema declaration so validators know which version to apply.
Path-Level Error Reporting
Generic error messages ("validation failed") slow debugging. Good validation tools report errors with the exact path to the failing field:
$.user.billing.country: must be at most 2 characters long (got "United States")
$.items[2].quantity: must be a positive integer (got -1)
$.status: must be one of "pending", "complete", "failed" (got "pending_approval")
The path ($.user.billing.country) lets you navigate directly to the problem without scanning the entire payload. This is what makes field-level schema validation dramatically faster than debugging runtime type errors.
Workflow: Format → Validate → Test
- Format the raw JSON payload with our JSON Formatter — fix syntax errors and make it readable
- Validate against your schema here — catch type mismatches, missing fields, and constraint violations
- Test field patterns with our Regex Tester if schema
patternconstraints need verification - Submit via cURL Generator and verify the response
This four-step flow catches errors at the cheapest possible point — before any network request, code execution, or database write.
Before sharing failing payloads in bug reports, sanitize them with our PII Redactor to remove production user data while preserving the structural information needed for debugging.
Experience it now.
Use the professional-grade JSON Schema Validator with zero latency and 100% privacy in your browser.