WW Tools
All references

JSON Schema Keywords by Draft

JSON Schema keywords changed between draft-07, 2019-09, and 2020-12: definitions became $defs, tuples moved to prefixItems, and keywords beside $ref started to apply. This sheet lists those differences, shows how to write optional and nullable fields, and explains when format is checked. The optional, nullable, and format tables match what the JSON Schema generator emits.

What changed between drafts

Featuredraft-072019-092020-12
$schema URIhttp://json-schema.org/draft-07/schema#https://json-schema.org/draft/2019-09/schemahttps://json-schema.org/draft/2020-12/schema
Reusable definitionsdefinitions$defs$defs
Tuplesitems as an array, plus additionalItemsitems as an array, plus additionalItemsprefixItems, plus items for the rest
Keywords beside $refIgnoredAppliedApplied
DependenciesdependenciesdependentRequired and dependentSchemasdependentRequired and dependentSchemas
Recursive referencesNone (plain $ref to #)$recursiveRef and $recursiveAnchor$dynamicRef and $dynamicAnchor
unevaluatedProperties, unevaluatedItemsNoYesYes
minContains, maxContainsNoYesYes
Plain-name anchors$id: "#name"$anchor$anchor
formatLeft to the implementationAnnotation by default. A validator may offer an option to assert itAnnotation by default. The format-assertion vocabulary or a validator option turns checking on
OpenAPINot used by OpenAPI 3.x (3.0 has its own dialect based on draft 5, with nullable)Not used by any OpenAPI versionOpenAPI 3.1 uses 2020-12

Optional and nullable

What the samples showedJSON Schema
Key in every sample, never null"type": "string", and the key is listed in required
Key missing from some samples"type": "string", and the key is left out of required
Key always present, sometimes null (simple type)"type": ["string", "null"], and the key is listed in required
Key always present, sometimes null (reused shape)"anyOf": [{ "$ref": "#/$defs/Address" }, { "type": "null" }], and the key is listed in required
Key missing somewhere and null somewhere"type": ["string", "null"], and the key is left out of required
OpenAPI 3.0 nullable: trueNot part of JSON Schema. Many validators ignore it, while Ajv (which the generator uses) accepts it as an extension when type is present. Portable JSON Schema, including OpenAPI 3.1, uses "type": ["string", "null"] or anyOf with null

required means the key must be present. It says nothing about the value, so a required key can hold null when its type allows it. The anyOf form is used around $ref because draft-07 ignores keywords beside $ref, and 2019-09 and later apply them as extra constraints, so neither can widen a $ref to also allow null. anyOf with a null branch does.

format values the generator detects

formatExampleDetected whenChecked when
date-time2024-03-01T09:30:00+05:30Every value is an RFC 3339 date and time with an offset (Z, +hh:mm, or -hh:mm, at most 23:59)Only when format assertion is on
date2024-02-29Every value is yyyy-mm-dd and a real calendar day, leap years includedOnly when format assertion is on
email[email protected]Every value is a plain address with a dotted domain of two or more labelsOnly when format assertion is on
urihttps://example.com/a?b=1#cEvery value is an http or https URL with a plain host nameOnly when format assertion is on
uuid7c9e6679-7425-40de-944b-e07fc1f90ae7Every value is 8-4-4-4-12 hexadecimal, in either caseOnly when format assertion is on
ipv4192.168.0.1Every value is four dotted octets from 0 to 255 with no leading zerosOnly when format assertion is on

A format is added only when every string seen in that position matched it. One value that does not match leaves the field as a plain string. In 2019-09 and 2020-12 a validator treats format as an annotation unless it is configured to assert it, which is what the Assert formats switch in the generator does.

Frequently asked questions

What does $schema do?

It names the draft (the dialect) the schema is written in, so a validator knows which keywords exist and what they mean. The same schema can validate differently under draft-07 and 2020-12, for example when keywords sit beside $ref. Leave it out and the validator falls back to its own default, which may not be the draft you wrote for.

Can I use a 2020-12 schema in OpenAPI 3.0?

Not as is. OpenAPI 3.0 uses its own subset of an older draft: type must be a single string, null is expressed with nullable: true, and keywords such as $defs, prefixItems, and const are not part of it. OpenAPI 3.1 aligned with 2020-12, so a 2020-12 schema fits a 3.1 document. One catch: a reference such as "#/$defs/Address" in a schema with no $id of its own resolves against the OpenAPI document root, where there is no $defs. Turn off Reuse repeated shapes in the generator before pasting into components/schemas, or rewrite "#/$defs/X" to "#/components/schemas/X".

What is the difference between $defs and definitions?

Both hold reusable subschemas that $ref points at, and neither affects validation on its own. definitions is the draft-07 name, from the validation specification. 2019-09 renamed it to $defs and moved it into the core vocabulary. A validator for the newer drafts still resolves a reference into definitions, since $ref works on any JSON Pointer, but new schemas should use $defs.

Use the tool