Description
For Java DTO properties, Springdoc does not preserve @Nullable when the property type is emitted as a component $ref.
This differs from Springdoc's KotlinNullablePropertyCustomizer, which wraps nullable $ref properties in a composed schema rather than modifying the referenced component.
The result is that a Java API can return null at runtime while the generated OpenAPI document declares a non-nullable property.
Minimal example
public record Outer(@Nullable Inner result) {}
public record Inner(String value) {}
With springdoc.api-docs.version=OPENAPI_3_0, the generated property is:
result:
$ref: "#/components/schemas/Inner"
Expected OpenAPI 3.0-compatible output:
result:
allOf:
- $ref: "#/components/schemas/Inner"
nullable: true
Runtime impact
The generated OpenAPI 3.0 document currently emits bare $ref properties for all of these fields.
Related issues
Proposed solution
Apply the same scoped wrapper strategy used by KotlinNullablePropertyCustomizer to recognized Java nullable annotations, including jakarta.annotation.Nullable:
- OAS 3.0: replace the property with
allOf: [ { $ref: ... } ] plus nullable: true.
- OAS 3.1: replace the property with a non-mutating union of the
$ref and the JSON Schema null type.
The component schema itself must remain unchanged, because it may be reused by non-nullable properties.
Environment
- Springdoc: 3.1.0
- Swagger Core: 2.2.52
- Spring Boot: 4.1.0
- Java: 21
- OpenAPI mode: 3.0
Description
For Java DTO properties, Springdoc does not preserve
@Nullablewhen the property type is emitted as a component$ref.This differs from Springdoc's
KotlinNullablePropertyCustomizer, which wraps nullable$refproperties in a composed schema rather than modifying the referenced component.The result is that a Java API can return
nullat runtime while the generated OpenAPI document declares a non-nullable property.Minimal example
With
springdoc.api-docs.version=OPENAPI_3_0, the generated property is:Expected OpenAPI 3.0-compatible output:
Runtime impact
The generated OpenAPI 3.0 document currently emits bare
$refproperties for all of these fields.Related issues
@Nullablefrom mutating the shared referenced component schema. However, that fix drops nullability from the referring Java property.Proposed solution
Apply the same scoped wrapper strategy used by
KotlinNullablePropertyCustomizerto recognized Java nullable annotations, includingjakarta.annotation.Nullable:allOf: [ { $ref: ... } ]plusnullable: true.$refand the JSON Schemanulltype.The component schema itself must remain unchanged, because it may be reused by non-nullable properties.
Environment