Skip to content

Proposal: introduce Schema.NullableMode enum to mirror RequiredMode / AccessMode pattern #5160

@thejeff77

Description

@thejeff77

Background

swagger-core has twice introduced enum-based "Mode" replacements for boolean fields on @Schema whose default false made it impossible to distinguish "user explicitly set false" from "not specified":

The nullable field has the same shape — boolean nullable() default false — and is now hitting the same problem class as required did pre-2022.

Why now

Auto-detection of nullable has expanded substantially in the last year:

Both auto-detection paths feed the same Schema.nullable boolean and have no way to express "user explicitly opts this property out of being marked nullable, regardless of heuristics." Users who try @Schema(nullable = false) find it has no effect (it's the default value and reflection can't distinguish it from absence). See springdoc/springdoc-openapi#3138 and the discussion under springdoc/springdoc-openapi#3256 for concrete user reports.

Proposal

Add to Schema:

/**
 * Allows specifying the nullable mode (NullableMode.AUTO, NULLABLE, NOT_NULLABLE).
 *
 * NullableMode.AUTO: lets the library decide based on heuristics (e.g. @Nullable
 *   annotations, downstream Kotlin T? detection in springdoc).
 * NullableMode.NULLABLE: forces the item to be considered nullable regardless
 *   of heuristics.
 * NullableMode.NOT_NULLABLE: forces the item to be considered not nullable
 *   regardless of heuristics.
 */
NullableMode nullableMode() default NullableMode.AUTO;

enum NullableMode {
    AUTO,
    NULLABLE,
    NOT_NULLABLE;
}

Deprecate boolean nullable() in favor of nullableMode(), mirroring the deprecation pattern used for required and readOnly/writeOnly.

Precedence

Following the precedent set in #4533 ("give precedence to requiredMode annotation"), nullableMode should take precedence over the legacy nullable boolean and over auto-detection heuristics. Suggested precedence:

  1. nullableMode = NULLABLE or NOT_NULLABLE → use that
  2. Legacy nullable = true (deprecated path) → treat as NULLABLE
  3. nullableMode = AUTO (default) → run heuristics (@Nullable annotations, downstream Kotlin reflection, etc.)

OAS 3.0 vs 3.1 mapping

Same as the existing handling for nullable: true:

  • OAS 3.0: sets nullable: true on simple types; for $ref, wraps in allOf with nullable: true sibling.
  • OAS 3.1: adds "null" to the type array; for $ref, wraps in oneOf with { "type": "null" }.

NOT_NULLABLE simply suppresses any of the above, regardless of what heuristics would otherwise have applied.

Impact on downstream auto-detection

The @Nullable auto-detection added in #5018 should defer to nullableMode != AUTO. Likewise, downstream libraries (springdoc's planned re-introduction of KotlinNullablePropertyCustomizer) can defer to the same field. This gives users a single, uniform escape hatch across all auto-detection sources.

Happy to follow up with a PR

If this direction is acceptable, I'm willing to put up a PR following the pattern of #4221 (RequiredMode introduction) and #4533 (precedence handling). Confirming intent here first since this is an annotation-surface change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions