API Documentation (Swagger/OpenAPI)
What Is OpenAPI/Swagger?
OpenAPI is a standard for describing REST APIs. SpringDoc OpenAPI automatically generates interactive documentation from Spring Boot code.
Swagger UI provides a web page for viewing and testing the API directly from a browser.
Adding the Dependency
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
Once added, restart the application. Swagger UI is available at:
http://localhost:8080/swagger-ui.html
Basic Configuration
# application.properties
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.api-docs.path=/v3/api-docs
springdoc.swagger-ui.filter=true
springdoc.packages-to-scan=com.example.controller
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("My Spring Boot API")
.version("1.0.0")
.description("API for the Spring Boot tutorial application")
.contact(new Contact()
.name("Luthfi")
.email("[email protected]")))
.addTagsItem(new Tag()
.name("User Management")
.description("CRUD operations for users"));
}
}
The @Operation Annotation
Describe each endpoint:
@RestController
@RequestMapping("/api/users")
@Tag(name = "User Management", description = "CRUD operations for users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
@Operation(summary = "Get all users", description = "Fetch the list of all users")
public ResponseEntity<List<UserResponseDTO>> getAllUsers() {
return ResponseEntity.ok(userService.getAllUsers());
}
@GetMapping("/{id}")
@Operation(summary = "Get user by ID", description = "Fetch user data by ID")
public ResponseEntity<UserResponseDTO> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
@Operation(summary = "Create user", description = "Create a new user")
public ResponseEntity<UserResponseDTO> createUser(
@RequestBody @Valid UserRequestDTO request) {
UserResponseDTO response = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
}
The @ApiResponse Annotation
Document the possible responses:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
@Operation(summary = "Get user by ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "User found",
content = @Content(schema = @Schema(implementation = UserResponseDTO.class))),
@ApiResponse(responseCode = "404", description = "User not found",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
public ResponseEntity<UserResponseDTO> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.getUserById(id));
}
@PostMapping
@Operation(summary = "Create new user")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "User created successfully"),
@ApiResponse(responseCode = "400", description = "Validation failed")
})
public ResponseEntity<UserResponseDTO> createUser(
@RequestBody @Valid UserRequestDTO request) {
UserResponseDTO response = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
}
The @Schema Annotation
Describe the properties of a DTO:
public class UserRequestDTO {
@Schema(description = "User full name", example = "John Smith", required = true)
private String name;
@Schema(description = "Email user", example = "[email protected]", required = true)
private String email;
@Schema(description = "Password user", example = "secretpass123", required = true)
private String password;
// getters, setters
}
public class UserResponseDTO {
@Schema(description = "Unique user ID", example = "1")
private Long id;
@Schema(description = "User full name", example = "John Smith")
private String name;
@Schema(description = "Email user", example = "[email protected]")
private String email;
@Schema(description = "Account creation time", example = "2024-01-15T10:30:00")
private LocalDateTime createdAt;
// getters, setters
}
Hiding Endpoints
Hide an endpoint from the documentation:
@Hidden
@GetMapping("/internal/metrics")
public ResponseEntity<Map<String, Object>> getInternalMetrics() {
return ResponseEntity.ok(metricsService.getMetrics());
}
Parameter Documentation
@GetMapping("/search")
@Operation(summary = "Search users")
public ResponseEntity<List<UserResponseDTO>> searchUsers(
@Parameter(description = "User name", example = "Mike")
@RequestParam(required = false) String name,
@Parameter(description = "Page", example = "0")
@RequestParam(defaultValue = "0") int page,
@Parameter(description = "Page size", example = "10")
@RequestParam(defaultValue = "10") int size) {
return ResponseEntity.ok(userService.search(name, page, size));
}
Testing the API via Swagger
Swagger UI provides a Try it out feature:
- Open
http://localhost:8080/swagger-ui.html - Click the endpoint you want to test
- Click the Try it out button
- Fill in the required parameters
- Click Execute
- See the response directly
Additional features:
| Feature | Description |
|---|---|
| Authorize | Set a bearer token or basic auth |
| Try it out | Test directly from the UI |
| Schema | View the request/response structure |
| Download | Export the OpenAPI spec (JSON/YAML) |
Exporting the OpenAPI Spec
# Download the JSON spec
curl http://localhost:8080/v3/api-docs -o openapi.json
# Download the YAML spec
curl http://localhost:8080/v3/api-docs?format=yaml -o openapi.yaml
You can use this spec for:
- Generating a client SDK (OpenAPI Generator)
- Importing into Postman
- Importing into other testing tools
Spring Security + Swagger
If you use Security, allow access to Swagger:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
With Swagger/OpenAPI, your API documentation is always up to date because it's generated from code. In the next chapter, we'll learn about Deployment of Spring Boot applications.