JSON Schema Explained: What It Is, Why You Need It, and How to Start
A practical guide to JSON Schema: what it is (a way to describe the shape of valid JSON), why it matters (catches bugs at the API boundary, generates docs, powers form libraries), the basic syntax, and how to start using it in your own projects.
JSON Schema is a way to describe the shape of valid JSON: which fields exist, what types they have, which are required, and what values are allowed. The description itself is a JSON document, so it can be edited, versioned, and shared the same way as any other data. The benefits: catch malformed data at the API boundary (before it hits the database), generate API documentation automatically, power form-generation libraries, and serve as a contract between teams. The <a href="/json-validator">JSON Validator</a> handles the syntax-level check; JSON Schema handles the structural check.
JSON Schema is one of those technologies that, once you understand it, makes every API integration easier. The promise: describe the shape of valid JSON in a JSON document, and use that description to validate data, generate documentation, power form libraries, and serve as a contract between teams. The reality: a few hours to learn, a few patterns to internalize, and a much more reliable data pipeline. This post covers what JSON Schema is, why it matters, the basic syntax, and how to start using it in your own projects.
What JSON Schema is
JSON Schema is a vocabulary for describing the structure of JSON data. The description is itself a JSON document, so it can be edited, versioned, and shared the same way as any other data file. The vocabulary covers:
- Types. A field can be a string, number, integer, boolean, array, object, or null. The schema specifies which type is expected.
- Required vs. optional fields. The schema lists which fields must be present and which can be omitted.
- Constraints. A string field can be constrained to a maximum length, a specific pattern (regex), or a fixed set of values (enum). A number field can be constrained to a minimum, maximum, or specific range.
- Nested structure. An object field can have its own schema; an array field can specify the schema of each item.
- References. A schema can reference another schema by name, allowing reuse across large schemas.
With these primitives, you can describe almost any JSON structure. The schema is the contract; the validator is the tool that checks the data against the contract.
Why it matters
The benefits of using JSON Schema, in rough order of impact:
- Catch errors at the API boundary. If the data is not valid against the schema, the API rejects it before the bad data reaches the database. The error is reported back to the caller with a precise reason.
- Auto-generated documentation. Tools like Swagger / OpenAPI read the schema and produce interactive API docs. The docs are always in sync with the schema, because the schema is the source of truth.
- Form generation. Libraries like react-jsonschema-form, JSON Forms, and Uniform read the schema and produce a working form. The form is generated from the schema, so adding a field to the schema adds a field to the form automatically.
- Client-side validation. The same schema can be used in the browser to validate form input before it is submitted. The user gets immediate feedback on malformed data, without a round trip to the server.
- Contract between teams. If team A produces JSON and team B consumes it, the schema is the contract. Both teams work from the same document, and disagreements are resolved by editing the schema, not by editing code.
For a system with one internal team and one internal API, the benefit is small. For a system with multiple teams, multiple services, or a public API, the benefit is large — the schema becomes the single source of truth for what the API expects and returns.
A worked example
A simple JSON Schema for a "user" object:
{
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 },
"role": { "type": "string", "enum": ["user", "admin", "moderator"] },
"active": { "type": "boolean" },
"tags": {
"type": "array",
"items": { "type": "string" },
"maxItems": 10
},
"address": {
"type": "object",
"properties": {
"street": { "type": "string" },
"city": { "type": "string" },
"zip": { "type": "string", "pattern": "^[0-9]{5}$" }
}
}
},
"required": ["id", "name", "email", "role"],
"additionalProperties": false
}
This schema describes a user object with:
- An integer
id(required, at least 1). - A string
name(required, 1-100 characters). - A string
email(required, validated as an email format). - An integer
age(optional, 0-150). - A string
role(required, one of "user", "admin", "moderator"). - A boolean
active(optional). - An array of strings
tags(optional, max 10 items). - An object
address(optional, with its own structure). - No additional properties beyond the listed ones (because
additionalProperties: false).
A validator reads this schema and checks any JSON document against it. The result is "valid" or a list of specific errors ("the 'role' field is required", "the 'email' field is not a valid email", "the 'tags' field has more than 10 items").
The basic syntax
The most-used keywords in JSON Schema:
type
Specifies the data type. The valid values are: string, number, integer, boolean, array, object, null. Can be a single string or an array (e.g. ["string", "null"] for a nullable string).
properties
For an object, lists the fields with their own schemas. Each property name is a key; the value is the schema for that field.
required
For an object, lists the field names that must be present. An array of strings.
items
For an array, specifies the schema of each item. Can be a single schema (all items have the same shape) or an array of schemas (tuple validation, where position matters).
enum
Constrains a value to one of a fixed set. {"enum": ["red", "green", "blue"]} means the value must be exactly one of those three strings.
minimum, maximum, exclusiveMinimum, exclusiveMaximum
For numbers, the inclusive or exclusive bounds. {"minimum": 0, "maximum": 100} means the number is between 0 and 100, inclusive.
minLength, maxLength
For strings, the minimum and maximum length.
pattern
For strings, an ECMA 262 regex. {"pattern": "^[0-9]{5}$"} matches exactly 5 digits.
format
A hint for the data format. Common values: email, uri, date, date-time, uuid, ipv4, ipv6. The validator may or may not enforce the format depending on the implementation.
additionalProperties
For an object, whether additional fields beyond the ones in properties are allowed. false means no; true (default) means yes; a schema means the additional fields must match the schema.
These keywords cover about 90% of real-world schemas. The remaining 10% is conditional schemas (if, then, else), references ($ref), and composition (allOf, anyOf, oneOf). Each is useful for specific cases; the basics are enough for most.
Common patterns
Polymorphism with oneOf
When a field can be one of several types (e.g. a "value" that is either a string or an integer), use oneOf with multiple schemas. The validator tries each schema and accepts the data if exactly one matches.
Reuse with $ref
When the same schema is used in multiple places, define it once and reference it by name. "$ref": "#/definitions/address" points to the schema named "address" in the current document's "definitions" section. This is the JSON Schema equivalent of a function call.
Conditional fields with if/then/else
When one field's schema depends on another's value, use if / then / else. "If the role is admin, then the permissions field is required; otherwise, it is forbidden." The validator applies the conditional schema based on the data.
Open vs. closed objects
For a strict API, set additionalProperties: false to reject unknown fields. For a flexible API, leave it as the default true to allow extensions. Most public APIs are strict; most internal APIs are flexible.
The ecosystem: validators, generators, and tools
The JSON Schema ecosystem is large and mature. The main categories:
- Documentation generators. Swagger / OpenAPI for REST APIs; ReDoc for human-readable output; Stoplight for interactive docs. Read the schema, produce the docs.
- Form generators. react-jsonschema-form, JSON Forms, Uniform, Formily. Read the schema, produce a working form with validation.
- Type generators. json-schema-to-typescript, quicktype, json-schema-codegen. Read the schema, produce TypeScript / Go / Rust / Java types.
- Mock data generators. json-schema-faker, openapi-sampler. Read the schema, produce realistic-looking mock data for testing.
The JSON Validator on this site handles the syntax-level check (is the JSON well-formed?). For the structural check (is the JSON valid against a schema?), use one of the validators above. The browser-based tool for the syntax check is enough for quick "did I write valid JSON" questions; for the schema-level check, a library is the right tool.
JSON Schema vs. TypeScript types
A common question: when you have TypeScript types, why do you need JSON Schema? The answer: the two are complementary, not redundant.
- TypeScript types are erased at runtime. They exist in the IDE for autocomplete and type checking, but they are not present in the compiled code. A bad API response is not caught by TypeScript at runtime.
The right pattern: use both. TypeScript types for the IDE and the developer experience. JSON Schema for the runtime validation. Generate one from the other (json-schema-to-typescript for the IDE direction, ts-json-schema-generator for the runtime direction) to keep them in sync.
JSON Schema vs. OpenAPI
OpenAPI is a specification for REST APIs. It uses JSON Schema for the request and response bodies, plus a lot of other stuff: paths, methods, authentication, servers, parameters, headers, content types. The two are layered: OpenAPI uses JSON Schema, and adds the REST-specific structure on top.
If you have an OpenAPI document, you have JSON Schema inside it (look for the components.schemas section). You can extract the JSON Schema and use it independently for validation, form generation, or any of the other use cases above. You do not need to use the full OpenAPI ecosystem to get the benefits of JSON Schema.
How to start using it
For an existing project, the lightest-weight way to start is to add a schema for one critical API endpoint:
- Write a JSON Schema for the request body and the response body.
- Validate the response body against the schema in tests. Catches accidental changes to the response shape that would break clients.
- Add more endpoints as the pattern proves out.
For a new project, start with the schema first. Write the schema, generate the TypeScript types from it, and use the types in the code. The schema is the source of truth; the types are derived. The same schema can be used in the browser to validate form input before submission, and in the API to validate the request before it hits the database.
The JSON Tree Viewer on this site helps you explore a complex JSON document — useful for understanding the structure of a schema or the data you are trying to match.
A short summary
- JSON Schema is a way to describe the shape of valid JSON. The description is itself a JSON document.
- Use it to catch errors at the API boundary, generate documentation, power form libraries, and serve as a contract between teams.
The benefits compound over time. The first endpoint you add a schema to catches one bug. The tenth endpoint catches ten bugs. The fiftieth endpoint is the API documentation, generated from the schemas, always in sync with the code. The cost is the schemas themselves, which are JSON documents and can be edited and versioned like any other data.