Why Document Your API?
Your API is useless if no one knows how to use it. Good documentation tells developers: what endpoints exist, what data to send, and what to expect back. Swagger generates this documentation automatically from your code.
Always Up-to-Date
Generated from code, so docs never become outdated.
Interactive Testing
Try API calls directly from the documentation page.
Client Generation
Generate SDKs for any language from OpenAPI spec.
Setup SpringDoc OpenAPI
<!-- pom.xml -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.3.0</version>
</dependency>
That's it! Now visit:
- Swagger UI: http://localhost:8080/swagger-ui.html
- OpenAPI JSON: http://localhost:8080/v3/api-docs
Basic Configuration
# application.properties springdoc.api-docs.path=/api-docs springdoc.swagger-ui.path=/swagger-ui.html springdoc.swagger-ui.operationsSorter=method springdoc.swagger-ui.tagsSorter=alpha
@Configuration
public class OpenAPIConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("User Management API")
.version("1.0")
.description("API for managing users and their profiles")
.contact(new Contact()
.name("API Support")
.email("support@example.com")))
.servers(List.of(
new Server().url("http://localhost:8080").description("Development"),
new Server().url("https://api.example.com").description("Production")
));
}
}
Documenting Controllers
@RestController
@RequestMapping("/api/users")
@Tag(name = "Users", description = "User management operations")
public class UserController {
@Operation(
summary = "Get all users",
description = "Returns a list of all registered users"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Successfully retrieved users"),
@ApiResponse(responseCode = "500", description = "Internal server error")
})
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
@Operation(summary = "Get user by ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "User found"),
@ApiResponse(responseCode = "404", description = "User not found")
})
@GetMapping("/{id}")
public User getUser(
@Parameter(description = "User ID", required = true, example = "1")
@PathVariable Long id) {
return userService.findById(id);
}
@Operation(summary = "Create new user")
@ApiResponse(responseCode = "201", description = "User created successfully")
@PostMapping
public ResponseEntity<User> createUser(
@RequestBody @Valid CreateUserRequest request) {
User user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
}
Documenting Request/Response Objects
@Schema(description = "Request to create a new user")
public class CreateUserRequest {
@Schema(description = "User's full name", example = "John Doe", required = true)
@NotBlank
private String name;
@Schema(description = "User's email address", example = "john@example.com")
@Email
private String email;
@Schema(description = "User's age", minimum = "18", maximum = "120")
@Min(18)
private Integer age;
@Schema(description = "Account type", allowableValues = {"BASIC", "PREMIUM", "ADMIN"})
private String accountType;
}
@Schema(description = "User response object")
public class UserResponse {
@Schema(description = "Unique identifier", example = "12345")
private Long id;
@Schema(description = "User's display name")
private String name;
@Schema(description = "Account creation date", example = "2024-01-15")
private LocalDate createdAt;
}
Authentication Documentation
@Configuration
public class OpenAPIConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info().title("Secure API").version("1.0"))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("Enter JWT token")));
}
}
// In controller - mark endpoint as secured
@Operation(summary = "Get current user profile",
security = @SecurityRequirement(name = "bearerAuth"))
@GetMapping("/profile")
public UserProfile getProfile() {
return userService.getCurrentProfile();
}
// Or mark as public (no auth required)
@Operation(summary = "Health check")
@SecurityRequirements // Empty = no security
@GetMapping("/health")
public String health() {
return "OK";
}
Grouping APIs
@Configuration
public class OpenAPIConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/api/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.pathsToMatch("/api/admin/**")
.addOpenApiCustomizer(openApi ->
openApi.info(new Info()
.title("Admin API")
.description("Administrative operations")))
.build();
}
}
Access at: /swagger-ui.html?group=public or /swagger-ui.html?group=admin
Common Annotations Reference
// Controller level
@Tag(name = "Users", description = "...") // Group endpoints
// Method level
@Operation(summary = "...", description = "...") // Describe endpoint
@ApiResponses({...}) // Document responses
@Hidden // Hide from docs
// Parameter level
@Parameter(description = "...", required = true, example = "...")
@RequestBody(description = "...", required = true)
// Model level
@Schema(description = "...", example = "...", required = true)
@Schema(hidden = true) // Hide field from docs