Articles

What Is JSON Schema? JSON Validation, Explained

JSON Schema is a vocabulary for describing and validating the shape of JSON data. How schemas work, where they show up, and the keywords that matter.

The Lycoris Team The Lycoris Team · · 5 min read
Clusters of structured data points

JSON Schema is a standard vocabulary for describing what a JSON document is allowed to look like — which keys must exist, what types they hold, what values are acceptable. A schema is itself just a JSON document, and a validator checks other JSON against it, returning either “valid” or a precise list of everything that’s wrong. If JSON is the data format the web runs on, JSON Schema is the contract that keeps that data honest.

A schema in action

Say your API accepts a signup payload. Here is a schema that pins down its shape:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["email", "plan"],
  "properties": {
    "email": {
      "type": "string",
      "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
    },
    "plan": {
      "type": "string",
      "enum": ["free", "pro", "enterprise"]
    },
    "seats": {
      "type": "integer",
      "minimum": 1
    }
  },
  "additionalProperties": false
}

This document passes:

{ "email": "morgan@example.com", "plan": "pro", "seats": 5 }

This one fails three ways — plan is missing, seats is below the minimum and the wrong type, and role isn’t an allowed key:

{ "email": "morgan@example.com", "seats": 0.5, "role": "admin" }

The point isn’t just the pass/fail verdict. A good validator reports each violation with its JSON path, so the client gets “seats must be an integer ≥ 1” instead of a mystery 400 error, and your handler code past the validation gate can trust the payload’s shape completely.

The keywords that matter

The vocabulary is large, but a dozen keywords cover the overwhelming majority of real schemas:

KeywordWhat it constrains
typeJSON type: object, array, string, number, integer, boolean, null
requiredObject keys that must be present
propertiesA schema for each named key
enumAn exact set of allowed values
patternA regex the string must match
minimum / maximumNumeric bounds
minLength / maxLengthString length bounds
itemsThe schema every array element must match
additionalPropertiesWhether keys not listed in properties are allowed
$refReuse another schema by reference

$ref is what makes schemas scale: define address or money once under $defs, reference it everywhere, and change it in one place.

Two more pieces of the vocabulary earn their keep in real APIs:

  • format annotates strings with well-known shapes — "format": "email", "date-time", "uuid", "uri". One caveat: the spec treats format as an annotation by default, so many validators ignore it unless you explicitly enable format checking. If your validation silently accepts "email": "not-an-email", this is usually why.
  • Composition keywordsoneOf, anyOf, allOf, not — combine schemas. The workhorse is oneOf for discriminated unions: a payment object that must match exactly one of the card, bank_transfer, or paypal schemas, typically told apart by a type field.

Where JSON Schema shows up

API boundaries. The classic use: validate every request body before your handler runs, so bad input fails fast at the edge instead of half-succeeding deep in business logic. The same applies to responses in contract tests — catching the moment a REST API starts returning a shape its consumers don’t expect.

OpenAPI. If you’ve used an API described by OpenAPI, you’ve used JSON Schema: OpenAPI describes the endpoints, methods, and auth, and delegates the request/response shapes to JSON Schema. Since OpenAPI 3.1 the two are fully aligned, so a schema written for one works in the other.

Editor autocomplete. When VS Code autocompletes keys in tsconfig.json or a CI workflow file, that’s a published JSON Schema at work — editors fetch the schema declared via the $schema key (or a public schema registry) and turn it into completions, hover docs, and inline red squiggles. Your own config files can get the same treatment by shipping a schema next to them.

Config validation. Because YAML parses into the same data structures as JSON, a JSON Schema validates YAML configs too — one schema can guard both a config.json and its YAML equivalent.

Form generation. A schema says “these fields, these types, these constraints” — which is enough for libraries to render a form, wire up its client-side validation, and keep it in sync with the server’s rules automatically.

Schemas as a source of truth for code

A schema is machine-readable, so tooling can go both directions:

  • Schema → types. Generators emit TypeScript interfaces, Python dataclasses, or Go structs from a schema, so your compile-time types and runtime validation can’t drift apart.
  • Types → schema. Frameworks derive schemas from your type or model definitions (Python’s Pydantic models are a well-known example), so you write the shape once and get validation, docs, and OpenAPI output for free.
  • Compiled validators. Libraries like Ajv (JavaScript) compile a schema into a specialized validation function ahead of time, making per-request validation cheap enough to run on every call.

This is the quiet superpower: the schema stops being documentation about the system and becomes an artifact the system is generated from.

Using it well

A few habits separate pleasant schema use from painful:

  • Validate at trust boundaries. Anything crossing from outside — API input, uploaded files, third-party webhooks, user-editable config — gets validated. Internal function-to-function data usually doesn’t need it.
  • Close your input schemas. Set additionalProperties: false on request bodies so typos like emial fail loudly instead of being silently ignored.
  • Keep schemas structural. “Discount can’t exceed 50% for free-plan users” is a business rule; encode it in code, not in increasingly baroque schema logic. Schemas are best at shape.
  • Version schemas with the API. A schema change is an interface change. Review it like one.

The takeaway

JSON Schema turns “this endpoint expects roughly this kind of object” into an enforceable, machine-readable contract: a JSON document that validates other JSON, powers editor autocomplete, generates types and forms, and underpins OpenAPI. A handful of keywords — type, required, properties, enum, $ref — covers most real-world use. Validate at the boundaries, keep business rules out of it, and the schema becomes the single source of truth that your docs, types, and runtime checks all derive from.

The Lycoris Team The Lycoris Team · · 5 min read

JSON vs YAML: Which Format Should You Use?

JSON and YAML represent the same data — the difference is syntax, strictness, and footguns. Where each format wins, and which to pick for configs and APIs.

#JSON #Web Development #Developer Tools
The Lycoris Team The Lycoris Team · · 6 min read

What Is JSON? The Data Format That Runs the Web

JSON is a lightweight text format for structured data — the default for APIs and config files. Syntax, examples, common mistakes, and why it beat XML.

#JSON #Web Development #Developer Tools
Takina Takina · · 4 min read

What Is a Lockfile? Reproducible Dependency Installs

A lockfile records the exact dependency versions your package manager resolved, so every install — from your laptop to CI — reproduces the same tree.

#JavaScript #Web Development #Developer Tools