🧩📜 OPENAPI 3.1 WITH SPRING BOOT 4: DOCUMENT YOUR API CONTRACT, NOT JUST YOUR CODE
🧩📜 OPENAPI 3.1 WITH SPRING BOOT 4: DOCUMENT YOUR API CONTRACT, NOT JUST YOUR CODE
When you build a REST API with Spring Boot 4, your controllers expose behavior.
But your consumers need something else:
▪️ What endpoints exist?
▪️ What request body is expected?
▪️ What response can be returned?
▪️ What errors are possible?
▪️ How is the API secured?
That is where OpenAPI 3.1 comes in. 🚀
🔸 TL;DR
▪️ OpenAPI 3.1 describes your REST API contract in a standard format.
- ▪️ Spring Boot 4 APIs can expose OpenAPI documentation using Springdoc OpenAPI 3.x.
- ▪️ Swagger UI is not the specification. It is the visual interface on top of the OpenAPI contract.
- ▪️ The most useful parts to document are metadata, operations, schemas, responses, parameters, groups, and security.
- ▪️ Good OpenAPI documentation reduces friction between backend, frontend, QA, DevOps, and external consumers.
🔸 WHAT IS OPENAPI?
OpenAPI is a standard, machine-readable description of an HTTP API.
It describes:
- ▪️ Paths
- ▪️ Operations
- ▪️ Parameters
- ▪️ Request bodies
- ▪️ Responses
- ▪️ Schemas
- ▪️ Security
- ▪️ Metadata
In short:
Your API contract, written in a format tools can understand.
With Spring Boot 4, the common approach is to use Springdoc OpenAPI 3.x to generate the OpenAPI document from your Spring MVC or WebFlux application.
🔸 1. ADD OPENAPI + SWAGGER UI
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>3.0.3</version>
</dependency>springdoc:
api-docs:
version: openapi_3_1
swagger-ui:
path: /swagger-ui.html
This enables:
▪️ /v3/api-docs → the OpenAPI JSON contract
▪️ /v3/api-docs.yaml → the YAML version
▪️ /swagger-ui.html → the visual UI for humans
OpenAPI is the contract. Swagger UI is the interactive documentation.
🔸 2. DESCRIBE THE API METADATA
import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; import org.springframework.context.annotation.Configuration; @Configuration @OpenAPIDefinition( info = @Info( title = "Order API", version = "1.0.0", description = "REST API for managing customer orders" ) ) class OpenApiConfig { }
This defines the global identity of your API.
Useful for:
▪️ API portals
▪️ Generated clients
▪️ Swagger UI
▪️ External consumers
▪️ Internal platform teams
Without metadata, your API documentation looks technical but unfinished.
🔸 3. DOCUMENT AN ENDPOINT OPERATION
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @Tag(name = "Orders", description = "Operations related to customer orders") @RestController @RequestMapping("/api/orders") class OrderController { @Operation( summary = "Create an order", description = "Creates a new order for an existing customer" ) @PostMapping @ResponseStatus(HttpStatus.CREATED) OrderResponse createOrder( @Valid @RequestBody CreateOrderRequest request ) { return new OrderResponse("ORD-123", "CREATED"); } }
Here you document the path + operation part of OpenAPI.
This helps developers understand:
▪️ What the endpoint does
▪️ Why it exists
▪️ How it should be used
▪️ Which business capability it exposes
Good documentation starts at the controller boundary.
🔸 4. DOCUMENT SCHEMAS WITH VALIDATION
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @Tag(name = "Orders", description = "Operations related to customer orders") @RestController @RequestMapping("/api/orders") class OrderController { @Operation( summary = "Create an order", description = "Creates a new order for an existing customer" ) @PostMapping @ResponseStatus(HttpStatus.CREATED) OrderResponse createOrder( @Valid @RequestBody CreateOrderRequest request ) { return new OrderResponse("ORD-123", "CREATED"); } }
This is the schema part of OpenAPI.
Springdoc can use your Java model and Jakarta Validation annotations to enrich the generated contract.
That means your DTO becomes more than a Java type.
It becomes documentation.
🔸 5. DOCUMENT RESPONSES CLEARLY
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @ApiResponses({ @ApiResponse( responseCode = "201", description = "Order created successfully" ), @ApiResponse( responseCode = "400", description = "Invalid request payload" ), @ApiResponse( responseCode = "404", description = "Customer not found" ) }) @PostMapping @ResponseStatus(HttpStatus.CREATED) OrderResponse createOrder(@Valid @RequestBody CreateOrderRequest request) { return new OrderResponse("ORD-123", "CREATED"); }
This documents the reality of APIs:
They do not only return 200 OK.
A useful API contract explains both:
▪️ Success cases
▪️ Failure cases
That is especially important for frontend teams, mobile teams, QA, integration partners, and generated clients.
🔸 6. DOCUMENT SECURITY
import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.security.SecurityScheme; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import org.springframework.context.annotation.Configuration; @Configuration @SecurityScheme( name = "bearerAuth", type = SecuritySchemeType.HTTP, scheme = "bearer", bearerFormat = "JWT" ) @OpenAPIDefinition( security = @SecurityRequirement(name = "bearerAuth") ) class OpenApiSecurityConfig { }
This declares that your API uses bearer token authentication.
Important point:
OpenAPI does not secure your API.
Spring Security does.
OpenAPI documents how consumers are expected to authenticate.
🔸 7. SPLIT YOUR API INTO GROUPS
import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration class OpenApiGroupsConfig { @Bean GroupedOpenApi publicApi() { return GroupedOpenApi.builder() .group("public") .pathsToMatch("/api/public/**") .build(); } @Bean GroupedOpenApi adminApi() { return GroupedOpenApi.builder() .group("admin") .pathsToMatch("/api/admin/**") .build(); } }
This is useful when one application exposes multiple API surfaces.
For example:
▪️ Public API
▪️ Admin API
▪️ Partner API
▪️ Internal API
One codebase. Multiple contracts.
🔸 OPENAPI VS SWAGGER
This is where many developers get confused.
▪️ OpenAPI = the specification / contract format
▪️ Swagger UI = a tool to visualize and test that contract
▪️ Swagger Editor = a tool to edit OpenAPI definitions
▪️ Swagger annotations = Java annotations used by libraries like Springdoc
▪️ Springdoc OpenAPI = the Spring Boot integration that generates the contract
So today, it is more precise to say:
“I generate an OpenAPI contract and expose it through Swagger UI.”
Not:
“I added Swagger.”
Swagger is the tooling ecosystem.
OpenAPI is the standard.
🔸 TAKEAWAYS
▪️ Do not treat OpenAPI as “nice documentation”. Treat it as an API contract.
▪️ Keep your DTO validation and schema documentation aligned.
▪️ Document errors as seriously as success responses.
▪️ Use groups when your API has different audiences.
▪️ Remember: OpenAPI documents the API. Spring Security protects it. Swagger UI displays it.
A clean API is not only one that works.
It is one that other developers can understand, test, and integrate without reverse-engineering your code. ☕
#Java #SpringBoot #SpringBoot4 #OpenAPI #OpenAPI31 #Swagger #Springdoc #RESTAPI #BackendDevelopment #APIDesign #SoftwareEngineering #JavaDeveloper
Go further with Java certification:
Java👇
Spring👇
SpringBook👇
JavaBook👇