flashman
← All guides

OpenAPI nullability across 3.0 and 3.1

Design OpenAPI nullability that stays valid across 3.0 nullable, 3.1 type unions, generators, and runtime validators without merging omission and null.

2026-09-30 · 8 min read

  • openapi
  • json-schema
  • null

Nullability is a wire contract, not a convenience of the language you generate. OpenAPI 3.0 expresses it with nullable beside a single type. OpenAPI 3.1 uses JSON Schema, so null belongs in the type array: ["string", "null"].

Those documents describe the same payload only when every tool in the path understands the spelling you chose. A keyword the parser ignores is an open door, not a constraint.

Decide presence and null separately

An omitted property, a JSON null, and an empty string are three states. Use required when the property must appear. Use the version's null spelling when the present value may be null. Document which state the server stores.

  • Required and nullable means the key is present and the value may be null.
  • Optional and non-null means the key may be absent and a present value has the declared type.
  • Avoid empty string as a stand-in for null unless the domain says so.
  • State the server default when a property is omitted.

Pick one OpenAPI generation and stick to it

Write the openapi version at the top of the document and keep null syntax inside that version. In 3.0, use a string type plus nullable. In 3.1, include null in type and do not rely on nullable, which JSON Schema does not define.

A oneOf of a typed schema and a null schema is a third spelling. Use it only when the chosen version and validator both implement it the way you tested. Mixing all three spellings in one component makes reviews guess.

Trace generators, bundles, and validators

Code generators differ on optional properties, union types, and JSON null. Pin the generator version and inspect the model it emits for a nullable field. A language optional that cannot represent null will drop the distinction you wrote.

Gateways and bundlers inline $ref and flatten allOf. Diff the authored component against the schema that actually validates traffic. A dropped nullable flag or a widened type array is a contract change even when the source file still looks right.

Migrate without a silent flag day

When moving a document from 3.0 to 3.1, convert each nullable field to a type union in the same change as the version bump. Keep fixtures that send null, omit the field, and send the wrong type.

Publish the migration to client owners before producers start sending nulls that old generated models reject or ignore. Dual-read on the server if some clients still omit the property and others send null.

Lock the contract with fixtures

Use Flashman's YAML/JSON converter for the specification, the JSON formatter for payloads, diff for bundled schemas and generated models, the case converter for naming drift, and the Markdown editor for the presence table.

Cover present null, omission, empty string, wrong types, 3.0 readers, 3.1 readers, inlined refs, allOf composition, and the generated client for each language you ship.

Try these tools