DevToolBoxGRATIS
Blog

Validasi JSON Schema: Tipe, Alat, dan Praktik Terbaik

14 menit bacaoleh DevToolBox

TL;DR

JSON Schema defines the structure and validation rules for JSON data. You can generate JSON Schema from any JSON automatically using our free tool, or write one manually. This guide covers every feature from basic types to advanced patterns like $ref, if/then/else, and allOf/oneOf, with real validation code for Node.js (Ajv) and Python (jsonschema).

Key Takeaways

  • JSON Schema validates JSON structure, types, and constraints using JSON itself
  • Use Draft 2020-12 for new projects (latest standard)
  • Core types: string, number, integer, boolean, null, object, array
  • Use $ref and $defs to keep schemas DRY and reusable
  • Ajv (JavaScript) and jsonschema (Python) are the most popular validators

What Is JSON Schema?

JSON Schema is a vocabulary that allows you to annotate and validate JSON documents. It describes the structure, data types, and constraints of your JSON data using JSON itself. Think of it as a contract or blueprint that defines what valid JSON data should look like.

JSON Schema is used for API request/response validation, configuration file validation, form generation, documentation, and code generation. It is supported by virtually every programming language and is defined in a series of IETF drafts, with the latest being Draft 2020-12.

Generate JSON Schema from any JSON data instantly with our free online tool.

Your First JSON Schema

Let us start with a simple example. Say you have a user object and want to validate its structure:

// The JSON data we want to validate
{
  "name": "Alice Johnson",
  "email": "alice@example.com",
  "age": 28,
  "active": true
}
// The JSON Schema that validates the data above
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "User",
  "description": "A user account",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "active": {
      "type": "boolean"
    }
  },
  "required": ["name", "email"],
  "additionalProperties": false
}

Basic Types

JSON Schema supports all JSON data types plus additional validation keywords for each:

// String type
{
  "type": "string",
  "minLength": 1,
  "maxLength": 255,
  "pattern": "^[a-zA-Z]+$",
  "format": "email"
}

// Number types
{
  "type": "number",
  "minimum": 0,
  "maximum": 100,
  "exclusiveMinimum": 0,
  "multipleOf": 0.01
}

{
  "type": "integer",
  "minimum": 1,
  "maximum": 65535
}

// Boolean
{
  "type": "boolean"
}

// Null
{
  "type": "null"
}

// Enum (fixed values)
{
  "type": "string",
  "enum": ["draft", "published", "archived"]
}

// Const (single fixed value)
{
  "const": "active"
}

// Multiple types (union)
{
  "type": ["string", "null"]
}

String Formats

Built-in String Formats:

Format          Example                     Description
--------------  -------------------------   -------------------
"email"         user@example.com            Email address
"uri"           https://example.com         Full URI
"uri-reference" /path/to/resource           URI or relative reference
"date"          2026-02-22                  ISO 8601 date
"date-time"     2026-02-22T14:30:00Z        ISO 8601 date-time
"time"          14:30:00Z                   ISO 8601 time
"duration"      P3Y6M4DT12H30M5S           ISO 8601 duration
"ipv4"          192.168.1.1                 IPv4 address
"ipv6"          ::1                         IPv6 address
"hostname"      example.com                 Internet hostname
"uuid"          550e8400-e29b-41d4-...      UUID
"regex"         ^[a-z]+$                    Regular expression
"json-pointer"  /foo/bar/0                  JSON Pointer (RFC 6901)

Objects: Required Fields and Properties

{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "description": "Unique user identifier"
    },
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 30,
      "pattern": "^[a-zA-Z0-9_]+$"
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "role": {
      "type": "string",
      "enum": ["admin", "editor", "viewer"],
      "default": "viewer"
    },
    "metadata": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    }
  },
  "required": ["id", "username", "email"],
  "additionalProperties": false,
  "minProperties": 3,
  "maxProperties": 10
}

Arrays: Items, MinItems, UniqueItems

// Array of strings
{
  "type": "array",
  "items": {
    "type": "string"
  },
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true
}

// Array of objects
{
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "name": { "type": "string" },
      "price": { "type": "number", "minimum": 0 }
    },
    "required": ["name", "price"]
  },
  "minItems": 1
}

// Tuple validation (fixed-length array with specific types)
{
  "type": "array",
  "prefixItems": [
    { "type": "string" },
    { "type": "integer" },
    { "type": "boolean" }
  ],
  "items": false,
  "minItems": 3,
  "maxItems": 3
}
// Valid: ["hello", 42, true]
// Invalid: ["hello", "world", true]

// Array containing specific values
{
  "type": "array",
  "contains": {
    "type": "string",
    "const": "admin"
  }
}
// Must contain at least one "admin" string

Nested Objects and Complex Structures

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Blog Post",
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "minLength": 5,
      "maxLength": 200
    },
    "slug": {
      "type": "string",
      "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
    },
    "content": {
      "type": "string",
      "minLength": 100
    },
    "author": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "email": { "type": "string", "format": "email" },
        "bio": { "type": "string", "maxLength": 500 }
      },
      "required": ["name", "email"]
    },
    "tags": {
      "type": "array",
      "items": { "type": "string", "minLength": 1 },
      "minItems": 1,
      "maxItems": 10,
      "uniqueItems": true
    },
    "metadata": {
      "type": "object",
      "properties": {
        "publishedAt": { "type": "string", "format": "date-time" },
        "updatedAt": { "type": "string", "format": "date-time" },
        "readingTime": { "type": "integer", "minimum": 1 },
        "featured": { "type": "boolean", "default": false }
      },
      "required": ["publishedAt"]
    },
    "status": {
      "type": "string",
      "enum": ["draft", "review", "published", "archived"]
    }
  },
  "required": ["title", "slug", "content", "author", "status"]
}

$ref: Reusable Schema Definitions

The $ref keyword allows you to reference and reuse schema definitions, keeping your schemas DRY (Don't Repeat Yourself):

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "state": { "type": "string", "minLength": 2, "maxLength": 2 },
        "zip": { "type": "string", "pattern": "^\\d{5}(-\\d{4})?$" },
        "country": { "type": "string", "default": "US" }
      },
      "required": ["street", "city", "state", "zip"]
    },
    "phone": {
      "type": "string",
      "pattern": "^\\+?[1-9]\\d{6,14}$"
    }
  },
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "billing_address": { "$ref": "#/$defs/address" },
    "shipping_address": { "$ref": "#/$defs/address" },
    "phone": { "$ref": "#/$defs/phone" },
    "alt_phone": { "$ref": "#/$defs/phone" }
  },
  "required": ["name", "billing_address"]
}

Conditional Schemas: if/then/else

JSON Schema supports conditional validation with if/then/else keywords. This lets you apply different validation rules based on the data:

{
  "type": "object",
  "properties": {
    "type": {
      "type": "string",
      "enum": ["individual", "company"]
    },
    "name": { "type": "string" },
    "tax_id": { "type": "string" },
    "company_name": { "type": "string" }
  },
  "required": ["type", "name"],

  "if": {
    "properties": {
      "type": { "const": "company" }
    }
  },
  "then": {
    "required": ["company_name", "tax_id"],
    "properties": {
      "tax_id": {
        "pattern": "^\\d{2}-\\d{7}$"
      }
    }
  },
  "else": {
    "properties": {
      "tax_id": {
        "pattern": "^\\d{3}-\\d{2}-\\d{4}$"
      }
    }
  }
}

// When type="company": company_name and tax_id (EIN format) are required
// When type="individual": tax_id uses SSN format

Composition: allOf, anyOf, oneOf, not

// allOf - must match ALL schemas (intersection)
{
  "allOf": [
    { "$ref": "#/$defs/base-entity" },
    {
      "properties": {
        "role": { "type": "string" }
      },
      "required": ["role"]
    }
  ]
}

// anyOf - must match at least ONE schema (union)
{
  "anyOf": [
    { "type": "string", "maxLength": 5 },
    { "type": "number", "minimum": 0 }
  ]
}
// Valid: "hello" (string <= 5 chars) or 42 (number >= 0)

// oneOf - must match EXACTLY ONE schema (exclusive)
{
  "oneOf": [
    {
      "type": "object",
      "properties": {
        "type": { "const": "email" },
        "address": { "type": "string", "format": "email" }
      },
      "required": ["type", "address"]
    },
    {
      "type": "object",
      "properties": {
        "type": { "const": "phone" },
        "number": { "type": "string" }
      },
      "required": ["type", "number"]
    }
  ]
}

// not - must NOT match the schema
{
  "not": {
    "type": "string",
    "pattern": "^admin"
  }
}
// Valid: "user_alice", 42
// Invalid: "admin_alice"

JSON Schema Draft Versions

JSON Schema Draft Version History:

Draft           Year    $schema URI                                    Key Features
-----------     ----    -------------------------------------------    -------------------------
Draft-04        2013    http://json-schema.org/draft-04/schema#        First widely adopted draft
Draft-06        2017    http://json-schema.org/draft-06/schema#        $ref improvements, const, contains
Draft-07        2018    http://json-schema.org/draft-07/schema#        if/then/else, readOnly, writeOnly
Draft 2019-09   2019    https://json-schema.org/draft/2019-09/schema   $defs, unevaluatedProperties
Draft 2020-12   2020    https://json-schema.org/draft/2020-12/schema   prefixItems, $dynamicRef

Recommendation: Use Draft 2020-12 for new projects.
Most validators support Draft-07 and later.

Validation in Node.js with Ajv

Ajv (Another JSON Validator) is the fastest JSON Schema validator for JavaScript and Node.js:

// Install: npm install ajv ajv-formats
const Ajv = require('ajv');
const addFormats = require('ajv-formats');

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);  // Adds "email", "date-time", "uri", etc.

// Define schema
const userSchema = {
  type: 'object',
  properties: {
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 0 },
    role: { type: 'string', enum: ['admin', 'user', 'guest'] },
  },
  required: ['name', 'email'],
  additionalProperties: false,
};

// Compile schema (do this once, reuse the validate function)
const validate = ajv.compile(userSchema);

// Validate data
const validData = { name: 'Alice', email: 'alice@example.com', age: 28, role: 'admin' };
const invalidData = { name: '', email: 'not-an-email', age: -5, extra: 'field' };

console.log(validate(validData));   // true
console.log(validate(invalidData)); // false
console.log(validate.errors);
// [
//   { keyword: 'minLength', message: 'must NOT have fewer than 1 characters' },
//   { keyword: 'format', message: 'must match format "email"' },
//   { keyword: 'minimum', message: 'must be >= 0' },
//   { keyword: 'additionalProperties', message: 'must NOT have additional properties' }
// ]

// Express.js middleware
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(userSchema), (req, res) => {
  // req.body is guaranteed to be valid here
  res.json({ success: true });
});

Validation in Python with jsonschema

# Install: pip install jsonschema
from jsonschema import validate, ValidationError, Draft202012Validator

schema = {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
        "name": {"type": "string", "minLength": 1},
        "email": {"type": "string", "format": "email"},
        "age": {"type": "integer", "minimum": 0},
        "tags": {
            "type": "array",
            "items": {"type": "string"},
            "uniqueItems": True,
        },
    },
    "required": ["name", "email"],
    "additionalProperties": False,
}

# Basic validation (raises exception on failure)
try:
    validate(
        instance={"name": "Alice", "email": "alice@example.com", "age": 28},
        schema=schema,
    )
    print("Valid!")
except ValidationError as e:
    print(f"Invalid: {e.message}")

# Collect all errors
validator = Draft202012Validator(schema)
errors = list(validator.iter_errors({"name": "", "email": "bad", "extra": 1}))
for error in errors:
    print(f"  - {error.json_path}: {error.message}")

# FastAPI integration (automatic JSON Schema validation)
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI()

class User(BaseModel):
    name: str
    email: EmailStr
    age: int | None = None
    tags: list[str] = []

@app.post("/users")
def create_user(user: User):
    # Pydantic auto-validates and generates JSON Schema
    return {"id": 1, "name": user.name}

# Access generated schema:
print(User.model_json_schema())

Common JSON Schema Patterns

// API Response envelope
{
  "type": "object",
  "properties": {
    "success": { "type": "boolean" },
    "data": {},
    "error": {
      "type": "object",
      "properties": {
        "code": { "type": "string" },
        "message": { "type": "string" }
      }
    },
    "pagination": {
      "type": "object",
      "properties": {
        "page": { "type": "integer", "minimum": 1 },
        "perPage": { "type": "integer", "minimum": 1, "maximum": 100 },
        "total": { "type": "integer", "minimum": 0 },
        "totalPages": { "type": "integer", "minimum": 0 }
      }
    }
  },
  "required": ["success"]
}

// Environment configuration
{
  "type": "object",
  "properties": {
    "NODE_ENV": { "enum": ["development", "staging", "production"] },
    "PORT": { "type": "integer", "minimum": 1, "maximum": 65535 },
    "DATABASE_URL": { "type": "string", "format": "uri" },
    "LOG_LEVEL": { "enum": ["debug", "info", "warn", "error"] },
    "CORS_ORIGINS": {
      "type": "array",
      "items": { "type": "string", "format": "uri" }
    }
  },
  "required": ["NODE_ENV", "PORT", "DATABASE_URL"]
}

Frequently Asked Questions

What is the difference between JSON Schema and TypeScript types?

TypeScript types are compile-time constructs that are erased when TypeScript is compiled to JavaScript. They provide no runtime validation. JSON Schema provides runtime validation that can be used to check incoming API data, user input, or configuration files. Many tools can generate TypeScript types from JSON Schema (and vice versa), so you can use both together.

Which JSON Schema draft should I use?

Use Draft 2020-12 for new projects. It has the best features including prefixItems for tuple validation, $dynamicRef for advanced composition, and cleaner semantics. Draft-07 is the most widely supported if you need maximum compatibility.

How do I validate optional fields with specific types?

Define the property in properties with its type constraints but do not include it in the required array. If the field is present, it must match the schema. If absent, validation passes. For nullable fields, use "type": ["string", "null"].

Can I generate JSON Schema from existing JSON data?

Yes. Many tools can infer a JSON Schema from sample data. Our JSON to JSON Schema generator analyzes your JSON and creates a matching schema automatically. You can then refine it by adding constraints like minLength, pattern, and required fields.

How do I handle recursive data structures in JSON Schema?

Use $ref to create recursive schemas. Define the schema in $defs and reference it from within itself. For example, a tree node that contains child nodes of the same type: define a "node" schema in $defs with a "children" property that is an array of {"$ref": "#/$defs/node"}.

Related Tools and Guides

𝕏 Twitterin LinkedIn
Apakah ini membantu?

Tetap Update

Dapatkan tips dev mingguan dan tool baru.

Tanpa spam. Berhenti kapan saja.

Coba Alat Terkait

JSJSON to JSON Schema GeneratorZDJSON to Zod Schema{ }JSON FormatterJSON Validator

Artikel Terkait

JSON ke TypeScript: Panduan Lengkap dengan Contoh

Pelajari cara mengonversi data JSON ke interface TypeScript secara otomatis. Mencakup objek bersarang, array, field opsional, dan praktik terbaik.

JSON Formatter & Validator: Format dan Validasi JSON Online

Formatter dan validator JSON gratis online. Format JSON, temukan kesalahan sintaks dengan contoh di JavaScript dan Python.

JSON to JSON Schema: Complete Guide to Generating Schemas from JSON Data

Learn how to generate JSON Schema from JSON automatically. Covers online converters, quicktype CLI, Python genson, JavaScript to-json-schema, Draft 2020-12 vs Draft-07, Ajv validation, and real-world use cases.