What Is JSON Schema?
JSON Schema is a vocabulary for describing and validating the structure of JSON data. A JSON Schema is itself a JSON document that specifies what a valid JSON instance must look like — which fields are required, what types they must be, what values are allowed, minimum and maximum lengths, and more.
Think of it as a contract between a data producer and a data consumer. If your API produces JSON that matches a published schema, any client that validates against the same schema knows exactly what to expect — no surprises, no runtime crashes.
JSON Schema is defined by an open standard. The current version is 2020-12.
Basic Schema Structure
An empty schema `{}` allows any valid JSON. A schema that requires the value to be a string:
{ "type": "string" }A schema for a user object with required fields and type constraints:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "User",
"type": "object",
"required": ["id", "name", "email"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 120 }
},
"additionalProperties": false
}Setting additionalProperties to false means any key not listed in properties will cause validation to fail. This is strict mode — useful for API request bodies where you want to reject unexpected fields.
JSON Schema Types
The type keyword accepts these values: string, number, integer, boolean, object, array, and null.
Use integer instead of number when decimals are not allowed. You can also allow multiple types: `"type": ["string", "null"]` for a nullable string.
String Constraints
{
"type": "string",
"minLength": 2,
"maxLength": 50,
"pattern": "^[A-Za-z0-9_]+$"
}The pattern property takes a regular expression. The format keyword validates well-known formats: email, uri, date, date-time, ipv4, uuid. Note that format validation is opt-in — validators must be configured to enforce it.
Number Constraints
{
"type": "number",
"minimum": 0,
"maximum": 100,
"multipleOf": 0.5,
"exclusiveMinimum": 0
}Use exclusiveMinimum instead of minimum when the boundary value itself is not allowed (value must be greater than 0, not greater than or equal to 0).
Enum — Restricting to Specific Values
The enum keyword restricts a value to a fixed list of allowed values:
{
"type": "string",
"enum": ["pending", "active", "suspended", "deleted"]
}This works for any type — enum can contain a mix of types: numbers, strings, booleans, and null.
Object Properties and Required Fields
{
"type": "object",
"required": ["productId", "quantity"],
"properties": {
"productId": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 },
"note": { "type": "string", "maxLength": 500 }
}
}Only productId and quantity are required — note is optional. If note is present, it must be a string of at most 500 characters.
Array Validation
{
"type": "array",
"minItems": 1,
"maxItems": 100,
"uniqueItems": true,
"items": {
"type": "string",
"minLength": 1
}
}The items keyword defines the schema that every element of the array must match.
Nested Objects
{
"type": "object",
"properties": {
"billing": {
"type": "object",
"required": ["street", "city", "country"],
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"state": { "type": "string" },
"pincode": { "type": "string", "pattern": "^[0-9]{6}$" },
"country": { "type": "string", "minLength": 2, "maxLength": 2 }
}
}
}
}Real-World Example: Order Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Order",
"type": "object",
"required": ["orderId", "userId", "items", "status", "createdAt"],
"properties": {
"orderId": { "type": "string" },
"userId": { "type": "integer" },
"status": { "type": "string", "enum": ["pending", "confirmed", "shipped", "delivered", "cancelled"] },
"totalAmount": { "type": "number", "minimum": 0 },
"currency": { "type": "string", "minLength": 3, "maxLength": 3 },
"createdAt": { "type": "string", "format": "date-time" },
"items": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["productId", "quantity", "unitPrice"],
"properties": {
"productId": { "type": "string" },
"name": { "type": "string" },
"quantity": { "type": "integer", "minimum": 1 },
"unitPrice": { "type": "number", "minimum": 0 },
"discount": { "type": "number", "minimum": 0, "maximum": 100 }
}
}
}
}
}A valid order document that matches this schema:
{
"orderId": "ORD-20250501-001",
"userId": 42,
"status": "confirmed",
"totalAmount": 1499.00,
"currency": "INR",
"createdAt": "2025-05-01T10:30:00Z",
"items": [
{ "productId": "PROD-101", "name": "Mechanical Keyboard", "quantity": 1, "unitPrice": 1499.00 }
]
}Validate in Node.js with Ajv
Ajv is the most popular JSON Schema validator for JavaScript and TypeScript:
import Ajv from "ajv";
import addFormats from "ajv-formats"; // for format: "email", "date-time", etc.
const ajv = new Ajv({ allErrors: true }); // collect ALL errors, not just first
addFormats(ajv);
const schema = {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 1 },
email: { type: "string", format: "email" },
},
};
const validate = ajv.compile(schema);
const data = { name: "Ravi", email: "not-an-email" };
const valid = validate(data);
if (!valid) {
console.error(validate.errors);
// [{ instancePath: "/email", message: "must match format "email"" }]
}Install with: `npm install ajv ajv-formats`
Validate in Python with jsonschema
import jsonschema
import json
schema = {
"type": "object",
"required": ["name", "email"],
"properties": {
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"}
}
}
data = {"name": "Ravi", "email": "ravi@example.com"}
try:
jsonschema.validate(instance=data, schema=schema)
print("Valid")
except jsonschema.exceptions.ValidationError as e:
print(f"Invalid: {e.message}")
except jsonschema.exceptions.SchemaError as e:
print(f"Schema error: {e.message}")Install with: `pip install jsonschema[format-nongpl]`
Why Use JSON Schema?
Without schema validation, your API may accept malformed data and fail much later — during database insertion, in a payment flow, or in a report. Schema validation catches problems at the boundary — the moment data enters your system — where fixing them is cheapest and safest.