Skip to content
Tools

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

KeywordApplies toMeaning
typeanystring, number, integer, boolean, object, array, null, or a list of these
propertiesobjectA schema for each named key
requiredobjectKeys that must be present (presence only, not non-null)
additionalPropertiesobjectfalse rejects unknown keys, or a schema for them
itemsarraySchema every item must match
minItems / maxItemsarrayLength limits
enum / constanyAllowed values / exactly one value
minLength / maxLength / patternstringLength and regular expression
minimum / maximumnumberInclusive range (exclusiveMinimum for strict)
$ref / $defsanyReuse a schema defined elsewhere
oneOf / anyOf / allOfanyCombine schemas

Three things that surprise beginners

  1. Unknown keys are allowed by default. Without additionalProperties: false, a typo like emial passes silently. Close objects you own; leave third-party payloads open so new fields do not break you.
  2. `required` is not inside `properties`. It is a sibling array on the object schema. Putting "required": true on a property was draft-03 syntax and is ignored by modern validators.
  3. `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 the ajv-formats package.

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:

  1. Paste a few representative records (as an array) into the JSON Schema generator to get types, properties and a required list.
  2. Remove keys from required that are genuinely optional; add null to types that can be null.
  3. Add the constraints a generator cannot guess: enum values, minimum, pattern, maxLength.
  4. Decide on additionalProperties per object.
  5. 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.

More JSON guides