Swagger / OpenAPI Docs
Good APIs ship with documentation that stays in sync with the code. springdoc-openapi inspects your Spring controllers at runtime and produces an OpenAPI 3 specification plus an interactive Swagger UI, with almost zero configuration. This page covers the dependency, the generated endpoints, enriching docs with annotations, and grouping multiple APIs.
Adding springdoc-openapi
One starter wires everything up for a Spring MVC application.
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.0</version>
</dependency>
Note: Use the
springdoc-openapi2.x line for Spring Boot 3.x. The olderspringfoxlibrary is unmaintained and does not support Boot 3 /jakarta.*. Pick...-webmvc-uifor servlet apps or...-webflux-uifor reactive apps.
What you get for free
Start the app and two endpoints appear automatically:
| Endpoint | What it serves |
|---|---|
/v3/api-docs | The OpenAPI 3 spec as JSON |
/v3/api-docs.yaml | The spec as YAML |
/swagger-ui.html | Interactive Swagger UI (redirects to /swagger-ui/index.html) |
Request:
curl http://localhost:8080/v3/api-docs
Output (excerpt):
{
"openapi": "3.0.1",
"info": { "title": "OpenAPI definition", "version": "v0" },
"paths": {
"/api/products/{id}": {
"get": {
"operationId": "findById",
"parameters": [
{ "name": "id", "in": "path", "required": true,
"schema": { "type": "integer", "format": "int64" } }
],
"responses": { "200": { "description": "OK" } }
}
}
}
}
Customizing the API info
Define a global OpenAPI bean to set title, version, contact, and servers.
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.info.*;
import org.springframework.context.annotation.*;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI productApi() {
return new OpenAPI()
.info(new Info()
.title("Product API")
.version("v1")
.description("Catalog and inventory endpoints")
.contact(new Contact().name("API Team").email("[email protected]")));
}
}
Or set basics via properties:
springdoc.swagger-ui.path=/docs
springdoc.api-docs.path=/v3/api-docs
springdoc.packages-to-scan=com.example.api
Annotating endpoints with @Operation
@Operation and @ApiResponse document a handler’s intent and possible responses, which Swagger UI renders.
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
@Tag(name = "Products", description = "Catalog operations")
public class ProductController {
@Operation(summary = "Get a product by id",
description = "Returns a single product or 404 if it does not exist")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Found"),
@ApiResponse(responseCode = "404", description = "Not found")
})
@GetMapping("/{id}")
public ProductResponse findById(@PathVariable Long id) {
return service.findById(id);
}
}
Describing models with @Schema
@Schema documents DTO fields — descriptions, examples, and constraints — directly on a record or class.
import io.swagger.v3.oas.annotations.media.Schema;
import java.math.BigDecimal;
public record ProductRequest(
@Schema(description = "Display name", example = "Mechanical Keyboard")
String name,
@Schema(description = "Unit price in USD", example = "89.99", minimum = "0")
BigDecimal price,
@Schema(description = "Optional details", maxLength = 280)
String description) {}
Bean Validation constraints (@NotBlank, @Positive, @Size) are also reflected into the schema automatically, so documented validation matches enforced validation.
Grouping APIs
Large applications split docs into named groups (e.g. public vs admin) using GroupedOpenApi beans. Each group gets its own entry in the Swagger UI dropdown.
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.*;
@Configuration
public class ApiGroups {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/api/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.pathsToMatch("/admin/**")
.build();
}
}
Documenting security
When the API is secured, declare the scheme so Swagger UI can send credentials in “Try it out.”
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
scheme = "bearer",
bearerFormat = "JWT")
@Configuration
public class OpenApiSecurityConfig { }
Tip: In production, disable or lock down Swagger UI. Set
springdoc.swagger-ui.enabled=false(or restrict the path via Spring Security) so the interactive console is not publicly exposed.
Pitfalls
- The wrong starter (webflux vs webmvc) yields a blank UI — match it to your stack.
springfoxdoes not work on Spring Boot 3; migrate tospringdoc-openapi.- Swagger UI left open in production leaks your full API surface — gate it behind auth or disable it.