Spring Boot OpenAPI 3 & Swagger UI

springdoc-openapi scans your Spring Boot application at startup and automatically generates an OpenAPI 3 spec from your @RestController methods, @RequestBody / @PathVariable parameters, and return types. Adding one dependency gets you a live JSON spec at /v3/api-docs and an interactive Swagger UI at /swagger-ui.html: no XML or YAML spec files to maintain manually.

Contents

Dependency & Quick Start

Add a single dependency: springdoc-openapi does the rest. The Swagger UI is available immediately without any extra configuration.

XML
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.5.0</version>
</dependency>

<!-- For WebFlux applications use: -->
<!-- springdoc-openapi-starter-webflux-ui -->
Properties
# Swagger UI URL: /swagger-ui.html  →  http://localhost:8080/swagger-ui.html
# OpenAPI JSON:   /v3/api-docs      →  http://localhost:8080/v3/api-docs

# Optional: change the paths
springdoc.swagger-ui.path=/docs
springdoc.api-docs.path=/api-spec

# Disable in production
springdoc.swagger-ui.enabled=false
If you have Spring Security, add the Swagger UI and API docs paths to your security permit list so they are accessible without authentication: .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()

Global API Info

Define title, version, description, and contact info for the entire API in one place using an OpenAPI bean.

Java
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
            .info(new Info()
                .title("Order Service API")
                .version("1.0.0")
                .description("Manages orders, products, and customer accounts.")
                .contact(new Contact()
                    .name("CsCode.io Team")
                    .email("api@example.com")
                    .url("https://example.com"))
                .license(new License()
                    .name("Apache 2.0")
                    .url("https://www.apache.org/licenses/LICENSE-2.0")));
    }
}

Annotating Endpoints

Use @Operation to describe individual endpoints, and @ApiResponse to document possible HTTP status codes. springdoc generates these automatically from code: annotations just add richer descriptions.

Java
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

@RestController
@RequestMapping("/orders")
@Tag(name = "Orders", description = "Operations for managing orders")
public class OrderController {

    @Operation(summary = "Get an order by ID",
               description = "Returns a single order matching the given ID.")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "Order found"),
        @ApiResponse(responseCode = "404", description = "Order not found"),
        @ApiResponse(responseCode = "401", description = "Unauthorized")
    })
    @GetMapping("/{id}")
    public ResponseEntity<Order> getOrder(
            @Parameter(description = "The order ID", example = "ORD-12345")
            @PathVariable String id) {
        return orderService.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @Operation(summary = "Create a new order")
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Order createOrder(@RequestBody Order order) {
        return orderService.save(order);
    }
}

Schema & Model Annotations

Annotate your model classes with @Schema to add descriptions, examples, and constraints to the generated spec.

Java
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "Represents a customer order")
public class Order {

    @Schema(description = "Unique order ID", example = "ORD-12345", accessMode = Schema.AccessMode.READ_ONLY)
    private String id;

    @Schema(description = "Product name", example = "Laptop", required = true)
    private String product;

    @Schema(description = "Quantity ordered", example = "2", minimum = "1")
    private int quantity;

    @Schema(description = "Current order status", allowableValues = {"PENDING","PROCESSING","COMPLETED","CANCELLED"})
    private String status;

    // getters/setters
}

Documenting Security (JWT Bearer)

Declare a Bearer token security scheme in the OpenAPI bean, then apply it globally or per-operation so the Swagger UI shows an "Authorize" button.

Java
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.Components;

@Bean
public OpenAPI customOpenAPI() {
    final String securitySchemeName = "bearerAuth";
    return new OpenAPI()
        .info(new Info().title("Order API").version("1.0"))
        // Apply Bearer auth globally to all endpoints
        .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
        .components(new Components()
            .addSecuritySchemes(securitySchemeName,
                new SecurityScheme()
                    .name(securitySchemeName)
                    .type(SecurityScheme.Type.HTTP)
                    .scheme("bearer")
                    .bearerFormat("JWT")));
}
Java
// Exclude specific endpoints from the security requirement
@Operation(summary = "Public health check", security = {})
@GetMapping("/health")
public String health() { return "OK"; }

API Groups

Group different sets of endpoints under separate Swagger UI tabs using GroupedOpenApi beans. Useful when the same application exposes both public and admin APIs.

Java
import org.springdoc.core.models.GroupedOpenApi;

@Bean
public GroupedOpenApi publicApi() {
    return GroupedOpenApi.builder()
        .group("public")
        .displayName("Public API")
        .pathsToMatch("/orders/**", "/products/**")
        .build();
}

@Bean
public GroupedOpenApi adminApi() {
    return GroupedOpenApi.builder()
        .group("admin")
        .displayName("Admin API")
        .pathsToMatch("/admin/**")
        .build();
}

Customization & Config

Key application.properties settings for controlling the spec and UI behaviour.

Properties
# Sort endpoints alphabetically in Swagger UI
springdoc.swagger-ui.operationsSorter=alpha

# Show request duration after each try-it-out call
springdoc.swagger-ui.display-request-duration=true

# Expand all operations by default
springdoc.swagger-ui.doc-expansion=full

# Include only specific packages in the spec
springdoc.packages-to-scan=com.example.api

# Exclude actuator endpoints from the spec
springdoc.paths-to-exclude=/actuator/**

# Display the try-it-out button for GET requests only
springdoc.swagger-ui.supported-submit-methods=get
Disable the Swagger UI in production (springdoc.swagger-ui.enabled=false) unless you have authentication protecting it. Exposing your full API schema publicly can aid attackers in exploring endpoints.