JSON Schema: A Beginner's Guide With Examples
· 4 min read
JSON Schema is a JSON document that describes what other JSON documents must look like: which keys are required, what type each value has, and which values are allowed. A validator checks data against the schema and reports every mismatch by path. The fastest way to start is to generate a draft from a sample with the JSON Schema generator and tighten it by hand.
A first schema
Here is a user record and a schema for it:
{ "id": 42, "email": "ada@example.com", "role": "admin", "tags": ["beta"] }{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "User",
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"role": { "enum": ["admin", "editor", "viewer"] },
"tags": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
},
"required": ["id", "email"],
"additionalProperties": false
}Read it top to bottom: the value must be an object; id must be a whole number of at least 1; role, if present, must be one of three strings; id and email must be present; and no other keys are allowed. Paste both into the JSON Schema validator and try removing email or setting role to "owner" to see the errors.
The keywords you will use most
| Keyword | Applies to | Meaning |
|---|---|---|
type | any | string, number, integer, boolean, object, array, null, or a list of these |
properties | object | A schema for each named key |
required | object | Keys that must be present (presence only, not non-null) |
additionalProperties | object | false rejects unknown keys, or a schema for them |
items | array | Schema every item must match |
minItems / maxItems | array | Length limits |
enum / const | any | Allowed values / exactly one value |
minLength / maxLength / pattern | string | Length and regular expression |
minimum / maximum | number | Inclusive range (exclusiveMinimum for strict) |
$ref / $defs | any | Reuse a schema defined elsewhere |
oneOf / anyOf / allOf | any | Combine schemas |
Three things that surprise beginners
- Unknown keys are allowed by default. Without
additionalProperties: false, a typo likeemialpasses silently. Close objects you own; leave third-party payloads open so new fields do not break you. - `required` is not inside `properties`. It is a sibling array on the object schema. Putting
"required": trueon a property was draft-03 syntax and is ignored by modern validators. - `format` is usually not enforced. In draft 2019-09 and 2020-12,
format(email, date-time, uri) is an annotation by default. Many validators only check it when asked, for example Ajv needs theajv-formatspackage.
Nullable and optional fields
A key not listed in required is optional. To allow null as a value, add it to the type list:
{ "manager_id": { "type": ["integer", "null"] } }OpenAPI 3.0 used nullable: true instead; OpenAPI 3.1 aligned with JSON Schema 2020-12 and uses the type list above.
Reusing definitions with $ref
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"address": {
"type": "object",
"properties": { "city": { "type": "string" }, "zip": { "type": "string" } },
"required": ["city"]
}
},
"type": "object",
"properties": {
"billing": { "$ref": "#/$defs/address" },
"shipping": { "$ref": "#/$defs/address" }
}
}#/$defs/address is a JSON Pointer into the same document. Older schemas use definitions instead of $defs; both work in most validators, but $defs is the 2019-09+ name.
Validating in code
JavaScript (Ajv)
import Ajv2020 from "ajv/dist/2020";
import addFormats from "ajv-formats";
const ajv = new Ajv2020({ allErrors: true });
addFormats(ajv);
const validate = ajv.compile(schema);
if (!validate(data)) {
console.log(validate.errors); // [{ instancePath: "/email", message: "must match format \"email\"" }, ...]
}Python (jsonschema)
from jsonschema import Draft202012Validator
validator = Draft202012Validator(schema)
for error in validator.iter_errors(data):
print(list(error.path), error.message)Compile the schema once and reuse the validator; compiling is the expensive step. Set allErrors (Ajv) or use iter_errors (Python) so users see every problem at once instead of one per attempt.
Generate first, then tighten
Writing a schema from scratch is slow. A practical workflow:
- Paste a few representative records (as an array) into the JSON Schema generator to get types, properties and a
requiredlist. - Remove keys from
requiredthat are genuinely optional; addnullto types that can be null. - Add the constraints a generator cannot guess:
enumvalues,minimum,pattern,maxLength. - Decide on
additionalPropertiesper object. - Test with good and bad samples in the JSON Schema validator, then add the schema to your repo and CI.
If you work in TypeScript, the same samples can produce types; compare approaches in JSON to TypeScript types.
FAQ
What is JSON Schema used for?
Validating API requests and responses, config files and data imports; documenting data formats (OpenAPI uses it); and generating forms, types and test data.
Which JSON Schema version should I use?
Draft 2020-12 is the current release and is what OpenAPI 3.1 uses. Draft-07 is still widely supported if a tool you depend on has not caught up.
How do I make a field required in JSON Schema?
List its name in the object's required array, next to properties. Required means the key must be present; it can still be null if the type allows it.
How do I disallow extra properties?
Set "additionalProperties": false on the object schema. Keys not listed in properties (or matched by patternProperties) then fail validation.
Can I generate a JSON Schema from JSON?
Yes. A generator infers types and required keys from samples. Review it afterwards: it cannot know enums, ranges or which fields are truly optional.

