swagger-core icon indicating copy to clipboard operation
swagger-core copied to clipboard

[Bug]: Native support for Jakarta @Nullable annotation to generate proper OAS 3.1 nullable types

Open DavDeDev opened this issue 4 months ago • 2 comments

Description

Springdoc-openapi currently requires explicit use of @Schema(nullable = true) or @Schema(types = {"string", "null"}) to generate proper nullable field definitions in OpenAPI specifications. However, our codebase already uses Jakarta's @Nullable annotation for null safety, which springdoc doesn't recognize automatically.

Proposed Solution

Add native support for detecting Jakarta's @Nullable annotation (and potentially other common nullable annotations) to automatically generate the proper OpenAPI 3.1 type definition with nullability.

Current Behavior

When a field is annotated with @Nullable, it's not properly reflected in the OpenAPI schema as nullable.

Desired Behavior

When a field is annotated with @Nullable, the generated schema should automatically use OpenAPI 3.1 syntax with type: ["string", "null"] or OpenAPI 3.0 syntax with nullable: true.

Benefits

  • Reduces annotation redundancy
  • Maintains DRY principle
  • Improves consistency between code and API documentation
  • Leverages existing null-safety annotations

Technical Notes

The implementation could scan for common nullable annotations including:

  • Jakarta's @Nullable
  • Spring's @Nullable
  • JetBrains' @Nullable
  • JSR-305 @Nullable

Reproducible Example

Model class demonstrating the issue:

@Getter
@Setter
public class ExampleModel {

    // This field uses only Jakarta @Nullable
    @Nullable
    private String stringThatCouldBeNull;

    // This field explicitly defines nullability with @Schema
    @Schema(nullable = true)
    private String explicitNullableString;

    // This field combines both approaches
    @Nullable
    @Schema(types = { "string", "null"})
    private String bothAnnotationsString;

    // Regular non-nullable field for comparison
    private String requiredString;
}

Controller exposing the model:

@RestController
@RequestMapping("/api/example")
@Tag(name = "Example API", description = "API to demonstrate @Nullable issue with springdoc-openapi")
public class ExampleController {

    @GetMapping
    @Operation(summary = "Get example model", description = "Returns an example model to demonstrate nullable field handling")
    public ResponseEntity<ExampleModel> getExample() {
        ExampleModel model = new ExampleModel();
        model.setStringThatCouldBeNull(null);
        model.setExplicitNullableString(null);
        model.setBothAnnotationsString(null);
        model.setRequiredString("This field is required");
        return ResponseEntity.ok(model);
    }
}

In the generated OpenAPI documentation, only fields with explicit @Schema annotations correctly show as nullable, while the field with only @Nullable is incorrectly shown as a required string type.

components:
  schemas:
    ExampleModel:
      type: object
      properties:
        stringThatCouldBeNull:
          type: string
        explicitNullableString:
          type: string
        bothAnnotationsString:
          type:
          - string
          - "null"
        requiredString:
          type: string

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.13'
    implementation 'jakarta.annotation:jakarta.annotation-api'
    implementation 'jakarta.validation:jakarta.validation-api'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

DavDeDev avatar Oct 20 '25 12:10 DavDeDev

please consider requiredMode too.

  @Nullable
  @Schema(
    nullable = true, 
    requiredMode = RequiredMode.NOT_REQUIRED
  )
  private String field;

neither of these 3 approaches work.

  • @Nullable
  • nullable = true in @schema
  • requiredMode = RequiredMode.NOT_REQUIRED in @Schema

https://github.com/springdoc/springdoc-openapi/issues/3138

zdary avatar Nov 15 '25 03:11 zdary

thank you @DavDeDev, @ewaostrowska for the unit test. I've reused it in the pull request to fix this issue. https://github.com/swagger-api/swagger-core/pull/5018

zdary avatar Nov 17 '25 01:11 zdary