How do you declare a nullable field in OpenAPI 3.0 versus OpenAPI 3.1?
answer
- 3.0 needed its own keyword
- 3.1 borrows the standard's type array
- Quote it, or YAML eats it
- Silently ignored after the version bump
- Reference siblings behave differently per version
basics
~10 sOpenAPI 3.0 uses its own keyword, nullable: true, alongside a single type. OpenAPI 3.1 removed nullable and follows JSON Schema 2020-12, where you write a type array such as type: [string, "null"].
solid answer
~40 sIn OpenAPI 3.0 a schema has exactly one `type`, so nullability needed an OpenAPI-specific extension: `type: string` plus `nullable: true`. That keyword does not exist in JSON Schema, which is part of why 3.0 schemas were not portable. OpenAPI 3.1 aligns the Schema Object with JSON Schema 2020-12, where `type` may be an array — so you write `type: ["string", "null"]`, and `nullable` is gone entirely. A 3.1 document containing `nullable: true` is not honoured: at best a linter warns, at worst tooling silently ignores it and your field stops accepting null. Two related gotchas: in 3.0, sibling keys next to a `$ref` are ignored, so making a referenced schema nullable needs an `allOf` wrapper, whereas 3.1 permits `$ref` siblings directly; and nullability is independent of `required`, which only governs key presence.
code
yaml · 9 lines# OpenAPI 3.0.3
closedAt:
type: string
format: date-time
nullable: true
address:
allOf:
- $ref: '#/components/schemas/Address'
nullable: truego deeper
Recall the two spellings and which version each belongs to: nullable: true in 3.0, type: [string, "null"] in 3.1.
Explain why the change happened — 3.1's alignment with JSON Schema 2020-12 — and that nullability and requiredness are independent axes.
Show the migration risk you would guard against: nullable silently becoming a no-op after the version bump, and generated clients turning an explicit null into an omitted field on PATCH.
Own the null policy across the estate — whether null is allowed to carry meaning at all, how PATCH semantics are expressed, and how a version migration is linted before it ships.
## Why the two versions differ at all OpenAPI 3.0's Schema Object was a modified subset of an old JSON Schema draft in which `type` held exactly one value. There was no way to say "a string or null". OpenAPI therefore added its own keyword, `nullable`, as an extension to the subset. OpenAPI 3.1 realigned the Schema Object with JSON Schema 2020-12, which supports `type` as an array of type names including `"null"`. With native support available, the bespoke keyword was removed. ## The 3.0 form ``` closedAt: type: string format: date-time nullable: true ``` `nullable: true` widens the declared type to also permit the JSON literal `null`. It defaults to `false`, so an ordinary `type: string` rejects null. ## The 3.1 form ``` closedAt: type: [string, "null"] format: date-time ``` Quote `"null"` in YAML — unquoted `null` parses as the YAML null value rather than the string `"null"`, and the schema then makes no sense. An equivalent but more verbose spelling is `oneOf: [{type: string}, {type: "null"}]`, which is sometimes needed when the null branch must sit next to other composition. ## The migration trap When you bump `openapi` from `3.0.3` to `3.1.0`, every `nullable: true` in the document becomes a no-op. `nullable` is not part of the 3.1 vocabulary; JSON Schema 2020-12 ignores keywords it does not know, so validation quietly narrows — a field that used to accept null now rejects it, and the failure appears at runtime in whichever consumer sends null first. Linters such as Spectral ship rules that flag `nullable` in a 3.1 document, and turning that rule on before the bump is the cheapest safeguard. ## nullable next to $ref This is the second-most-asked follow-up. In OpenAPI 3.0, a Reference Object's siblings are ignored: writing ``` address: $ref: '#/components/schemas/Address' nullable: true ``` does not make the referenced schema nullable — the `$ref` wins and the sibling is dropped. The 3.0 workaround is to wrap it: ``` address: allOf: - $ref: '#/components/schemas/Address' nullable: true ``` The single-element `allOf` exists purely to give `nullable` a place to live. This is why 3.0 documents are littered with one-branch `allOf` wrappers that also carry `description` or `example`. In OpenAPI 3.1, Schema Objects follow JSON Schema 2020-12, where `$ref` may coexist with sibling keywords, so the wrapper is unnecessary for schema-level annotations. ## nullable is not optional Nullability and requiredness are separate axes. `required: [closedAt]` with a nullable schema means "the key must be present, and null is a legitimate value". Dropping the field from `required` means "the key may be absent". Modelling both — optional *and* nullable — leaves consumers unable to distinguish absent from null unless their client library exposes a tri-state, which most do not. Decide which of the two states you actually need. ## What generators do Generators map nullability to the target language's null handling: a nullable field becomes a nullable type, an `Optional`, or a type union. Where it bites is round-tripping — a generated client that deserializes null into a language null and then re-serializes may emit the field as absent rather than null, silently converting "clear this value" into "leave it unchanged" on a PATCH. If null carries meaning in your API, test the round trip through the generated client rather than trusting the schema alone. ## Neighbouring 3.1 changes worth knowing The `nullable` removal is one item in a family of JSON Schema realignments: `exclusiveMinimum` and `exclusiveMaximum` become numbers in 3.1 rather than booleans modifying `minimum`/`maximum`, and the Schema Object's singular `example` is deprecated in favour of the JSON Schema `examples` array. If you are auditing a document for a 3.1 move, sweep for all of these together — they are the changes a mechanical converter is most likely to leave behind.
- What happens to nullable: true when you bump a document from 3.0 to 3.1?It becomes a no-op. `nullable` is not in the 3.1 vocabulary, and JSON Schema 2020-12 ignores unknown keywords, so the field silently stops accepting null. Lint for `nullable` before the bump and rewrite each occurrence as a type array.
- Why does nullable next to a $ref not work in OpenAPI 3.0?Because 3.0 treats a Reference Object's siblings as ignored — the `$ref` replaces the object wholesale. The workaround is a single-element `allOf` wrapping the `$ref` with `nullable` beside it. OpenAPI 3.1 permits `$ref` siblings directly, so the wrapper disappears.
- Is a nullable field the same as an optional one?No. Nullability is about the value; requiredness is about whether the key appears. A required nullable field must be present and may be null; an optional field may be missing entirely. Modelling both leaves consumers unable to distinguish absent from null.
saying these in an interview costs you the question
- Keeps nullable: true in a 3.1 document
- Writes unquoted null in a YAML type array
- Puts nullable beside a $ref in OpenAPI 3.0
- Says nullable makes a field optional
- Assumes 3.0 type can be an array