JSON Schema Validation Guide: Validate Your Data Structure
Every application that receives data from external sources faces the same fundamental question: can I trust this data? Whether it is an API request body, a configuration file, a message from a queue, or a data import, untrusted data must be validated before it enters your system. JSON Schema provides a standardized, language-independent way to define what valid data looks like and to verify that incoming data conforms to that definition. This guide covers everything from writing your first schema to advanced patterns for complex validation, with practical examples you can apply immediately.
What Is JSON Schema?
JSON Schema is a JSON document that describes the structure and constraints of other JSON documents. It is a vocabulary that allows you to annotate and validate JSON data, standardized through the Internet Engineering Task Force (IETF). A JSON Schema defines rules about what fields must exist, what types they must be, what values are acceptable, and how objects and arrays should be structured.
Think of JSON Schema as a contract for your data. Just as a database schema defines the columns, types, and constraints of a table, a JSON Schema defines the properties, types, and constraints of a JSON document. Any JSON data that satisfies all the constraints in a schema is called a valid instance, while data that violates any constraint is invalid.
What JSON Schema Is Not
- Not a data format: JSON Schema does not define how data is serialized or transmitted. It only describes what valid data looks like.
- Not a replacement for business logic: Schema validation checks structural correctness (types, formats, ranges). Complex business rules (like "a user cannot transfer more than their balance") belong in application code.
- Not a database schema: While the concepts are similar, JSON Schema validates JSON documents, not database tables. However, you can use JSON Schema alongside database constraints for defense in depth.
JSON Schema Versions
JSON Schema has evolved through several drafts, each adding features and refining the vocabulary. Understanding the versions helps you choose the right one for your project and avoid compatibility issues.
| Version | $schema URI | Status | Key Features |
|---|---|---|---|
| Draft 2020-12 | https://json-schema.org/draft/2020-12/schema | Current | prefixItems, dynamicRef, vocabulary support |
| Draft 2019-09 | https://json-schema.org/draft/2019-09/schema | Stable | unevaluatedProperties, $recursiveRef |
| Draft 7 | http://json-schema.org/draft-07/schema# | Widely supported | if/then/else, contentEncoding |
| Draft 6 | http://json-schema.org/draft-06/schema# | Legacy | propertyNames, contains |
| Draft 4 | http://json-schema.org/draft-04/schema# | Deprecated | Original widely-adopted version |
Writing Your First Schema
Let us start with a simple example: a schema for a user object with a name, email, and age.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/user.json",
"title": "User",
"description": "A user account in the system",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "The user's full name"
},
"email": {
"type": "string",
"format": "email",
"description": "The user's email address"
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150,
"description": "The user's age in years"
}
},
"required": ["name", "email"],
"additionalProperties": false
}
This schema declares that a valid user must be an object with
name and email as required string
properties. The age property is optional but must be an
integer between 0 and 150 if present. The
additionalProperties: false constraint prevents any
properties not defined in the schema, which catches typos and
unexpected fields.
Core Keywords Reference
JSON Schema provides a rich vocabulary of keywords for defining constraints. Here are the most important ones organized by category.
Type Keywords
| Keyword | Description | Example |
|---|---|---|
type
|
Expected data type(s) |
"type": "string" or
"type": ["string", "null"]
|
enum
|
List of allowed values |
"enum": ["active", "inactive", "pending"]
|
const
|
Must equal this exact value |
"const": "v2"
|
Number Constraints
| Keyword | Description | Example |
|---|---|---|
minimum
|
Minimum value (inclusive) |
"minimum": 0
|
exclusiveMinimum
|
Minimum value (exclusive) |
"exclusiveMinimum": 0
|
maximum
|
Maximum value (inclusive) |
"maximum": 100
|
exclusiveMaximum
|
Maximum value (exclusive) |
"exclusiveMaximum": 100
|
multipleOf
|
Must be a multiple of this value |
"multipleOf": 0.01
|
String Constraints
| Keyword | Description | Example |
|---|---|---|
minLength
|
Minimum string length |
"minLength": 1
|
maxLength
|
Maximum string length |
"maxLength": 255
|
pattern
|
Regex pattern the string must match |
"pattern": "^[A-Z]{2}\\d{4}$"
|
format
|
Semantic format (email, uri, date-time, etc.) |
"format": "email"
|
Object Constraints
| Keyword | Description | Example |
|---|---|---|
properties
|
Schema for each known property |
"properties": {"name": {"type": "string"}}
|
required
|
List of mandatory properties |
"required": ["name", "email"]
|
additionalProperties
|
Whether extra properties are allowed |
"additionalProperties": false
|
minProperties
|
Minimum number of properties |
"minProperties": 1
|
maxProperties
|
Maximum number of properties |
"maxProperties": 10
|
patternProperties
|
Schemas for properties matching a regex |
"patternProperties": {"^S_": {"type": "string"}}
|
Array Constraints
| Keyword | Description | Example |
|---|---|---|
items
|
Schema for all array items |
"items": {"type": "string"}
|
prefixItems
|
Schemas for positional items (Draft 2020-12) |
"prefixItems": [{"type": "string"}, {"type": "number"}]
|
minItems
|
Minimum number of items |
"minItems": 1
|
maxItems
|
Maximum number of items |
"maxItems": 100
|
uniqueItems
|
All items must be unique |
"uniqueItems": true
|
Common Schema Patterns
Real-world schemas often require patterns beyond simple type checking. Here are the most frequently needed patterns.
Conditional Validation with if/then/else
Use conditional logic to apply different constraints based on the value of a property. For example, a payment object requires different fields depending on the payment method.
{
"type": "object",
"properties": {
"method": { "enum": ["credit_card", "bank_transfer"] },
"card_number": { "type": "string" },
"routing_number": { "type": "string" }
},
"required": ["method"],
"if": {
"properties": { "method": { "const": "credit_card" } }
},
"then": {
"required": ["card_number"]
},
"else": {
"required": ["routing_number"]
}
}
Composition with allOf, anyOf, oneOf
Composition keywords allow you to combine schemas in powerful ways:
- allOf: Data must satisfy ALL sub-schemas. Use this to combine multiple constraints or mix in reusable schema fragments.
- anyOf: Data must satisfy AT LEAST ONE sub-schema. Use this for union types where multiple shapes are acceptable.
- oneOf: Data must satisfy EXACTLY ONE sub-schema. Use this for mutually exclusive alternatives.
{
"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": "^\\+?[1-9]\\d{1,14}$" }
},
"required": ["type", "number"]
}
]
}
Reusing Schemas with $ref
The $ref keyword allows you to reference and reuse
schemas, eliminating duplication and keeping your schemas
maintainable. You can reference schemas within the same document or
external files.
{
"$id": "https://example.com/schemas/order.json",
"type": "object",
"properties": {
"customer": { "$ref": "#/$defs/address" },
"shipping": { "$ref": "#/$defs/address" },
"billing": { "$ref": "#/$defs/address" },
"items": {
"type": "array",
"items": { "$ref": "#/$defs/lineItem" },
"minItems": 1
}
},
"required": ["customer", "items"],
"$defs": {
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
},
"required": ["street", "city", "zip", "country"]
},
"lineItem": {
"type": "object",
"properties": {
"product": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 },
"price": { "type": "number", "exclusiveMinimum": 0 }
},
"required": ["product", "quantity", "price"]
}
}
}
Nullable Types
In JSON Schema Draft 2020-12, nullable types are expressed using an
array of types that includes "null":
{
"type": ["string", "null"],
"description": "An optional display name, or null if not set"
}
In older drafts, the nullable: true keyword was used in
OpenAPI specifications. For standard JSON Schema, always use the type
array approach.
Format Validation
The format keyword provides semantic validation beyond
structural checks. It specifies that a string must conform to a
well-known format. Commonly supported formats include:
| Format | Description | Example |
|---|---|---|
email
|
Email address | user@example.com |
uri
|
Valid URI | https://example.com/path |
uri-reference
|
URI or relative reference | /path/to/resource |
date-time
|
ISO 8601 date-time | 2026-05-19T14:30:00Z |
date
|
ISO 8601 date | 2026-05-19 |
time
|
ISO 8601 time | 14:30:00Z |
ipv4
|
IPv4 address | 192.168.1.1 |
ipv6
|
IPv6 address | ::1 |
uuid
|
Universally unique identifier | 550e8400-e29b-41d4-a716-446655440000 |
hostname
|
Internet hostname | www.example.com |
format keyword is an annotation, not a constraint.
Validators may ignore it unless you explicitly enable format
validation. In Ajv, pass { strict: true } or
{ validateFormats: true } to enforce format checking.
Always verify that your validator enforces format constraints.
Validating JSON Data Programmatically
Schema validation is most useful when integrated into your application code. Here are examples using popular libraries in different languages.
JavaScript with Ajv
Ajv is the most widely used JSON Schema validator for JavaScript. It supports all draft versions and offers excellent performance through JIT compilation of schemas.
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv();
addFormats(ajv);
const schema = {
type: 'object',
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0 }
},
required: ['name', 'email'],
additionalProperties: false
};
const validate = ajv.compile(schema);
const valid = validate({ name: 'Alice', email: 'alice@example.com', age: 30 });
if (!valid) {
console.log(validate.errors);
// [{ keyword: 'required', params: { missingProperty: 'email' }, ... }]
}
Python with jsonschema
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0}
},
"required": ["name", "email"],
"additionalProperties": False
}
try:
validate(instance={"name": "Alice", "email": "alice@example.com"}, schema=schema)
except ValidationError as e:
print(f"Validation failed: {e.message}")
API Data Validation with JSON Schema
One of the most valuable applications of JSON Schema is validating API request and response data. This ensures that your API receives well-formed input and returns data in the expected format, catching errors early and providing clear error messages to clients.
Express.js Middleware
import Ajv from 'ajv';
const ajv = new Ajv({ allErrors: true });
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
});
}
next();
};
}
app.post('/api/users',
validateBody({
type: 'object',
properties: {
name: { type: 'string', minLength: 1, maxLength: 100 },
email: { type: 'string', format: 'email' },
role: { enum: ['admin', 'editor', 'viewer'] }
},
required: ['name', 'email'],
additionalProperties: false
}),
(req, res) => {
// req.body is guaranteed valid here
createUser(req.body);
}
);
OpenAPI and JSON Schema
OpenAPI (formerly Swagger) uses a subset of JSON Schema for defining API request and response schemas. If you are already using OpenAPI, you can extract the schemas from your API specification and use them for runtime validation. Tools like openapi-schema-validator and express-openapi-validator automate this process, ensuring that your API documentation and validation logic always stay in sync.
Best Practices
-
Always set
$schema: Include the$schemakeyword to declare which draft your schema uses. This prevents ambiguity and ensures validators interpret your schema correctly. -
Use
$idfor schema identity: The$idkeyword provides a unique identifier for your schema and serves as the base URI for resolving$refreferences. Always set it, even for local schemas. -
Set
additionalProperties: false: By default, JSON Schema allows any additional properties in objects. SettingadditionalProperties: falsecatches typos and unexpected fields, making your schema stricter and your API more predictable. -
Use
$defsfor reusable components: Define common schemas in$defsand reference them with$ref. This reduces duplication and makes schemas easier to maintain. -
Add descriptions: Every property and schema should
have a
descriptionfield. This serves as documentation and helps other developers understand the purpose and constraints of each field. - Validate at system boundaries: Apply schema validation at the edges of your system: API endpoints, message consumers, data import pipelines, and configuration loaders. Do not validate data that your own code has already produced and trusts.
- Enable strict mode in validators: Configure your validator to reject unknown keywords, enforce format validation, and report all errors rather than stopping at the first one. This catches more issues in a single validation pass.
-
Version your schemas: Include the schema version in
the
$idURI (e.g.,https://example.com/schemas/user/v2.json). This allows you to evolve schemas without breaking existing consumers. - Test your schemas: Write unit tests for your schemas with both valid and invalid examples. This ensures your schemas enforce the constraints you intend and catches regressions when schemas are modified.
-
Use format for semantic validation: Prefer
format: "email"over a complex regex pattern. Formats are more readable, more maintainable, and benefit from well-tested validation logic in your validator library.
Need to validate JSON data against a schema? Try our free online JSON Schema Validator. Paste your schema and data to get instant validation results with detailed error messages.
JSON Schema Validator JSON FormatterFrequently Asked Questions
What is JSON Schema?
JSON Schema is a JSON document that describes the structure and constraints of other JSON documents. It lets you define required fields, expected data types, value ranges, string patterns, and nested object structures. You can use it to validate incoming data, generate documentation, and create form interfaces automatically.
What version of JSON Schema should I use?
Use JSON Schema Draft 2020-12 for new projects. It is the latest stable version and is supported by major validation libraries like Ajv. Draft 7 is also widely supported if you need compatibility with older tools. Avoid Draft 4 and earlier as they use outdated keywords and lack modern features.
How is JSON Schema different from TypeScript interfaces?
TypeScript interfaces provide compile-time type checking within TypeScript code only. JSON Schema provides runtime validation that works across programming languages and can validate data from any source (API requests, files, databases). Use TypeScript for development-time safety and JSON Schema for runtime data validation at system boundaries.
Can JSON Schema validate API request bodies?
Yes, JSON Schema is widely used for API request and response validation. Frameworks like Express (with express-json-validator), FastAPI, and Spring Boot support JSON Schema validation natively or through middleware. Validating request bodies against a schema ensures that incoming data has the correct structure before your application processes it.
What are the most important JSON Schema keywords?
The most essential keywords are: type (data type), properties (object fields), required (mandatory fields), items (array element schema), minimum/maximum (number bounds), minLength/maxLength (string bounds), pattern (regex for strings), enum (allowed values), and $ref (reference to reuse schemas). These cover the vast majority of validation needs.