springdoc-openapi について
Spring Boot の Java プロジェクト向けに、springdoc-openapi を使った API ドキュメントと Swagger UI の生成・設定方法を案内するスキルです。エンドポイント、モデル、セキュリティ要件、API グループの記述を扱います。
主な機能
- 01API ドキュメントと Swagger UI の設定
- 02アノテーションによるエンドポイントとモデルの記述
- 03セキュリティとレスポンスの文書化
- 04タグやパスによる API のグループ化
- 05内部エンドポイントの非表示
- 06設定上の一般的な問題の調査
活用シーン
- 01Spring Boot API への OpenAPI ドキュメント追加
- 02DTO フィールドと列挙型の説明
- 03公開・内部エンドポイントの整理
- 04Swagger UI やスキーマの問題の診断
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ claude-dev-suite/ claude-dev-suite --skill springdoc-openapiインストール後、Claude Code で/springdoc-openapiを入力して読み込まれたか確認します。
詳細
Springdoc OpenAPI(Swagger)
詳細な知識:包括的なドキュメントを参照するには、technology に
springdoc-openapiを指定してmcp__documentation__fetch_docsを使用してください。
Maven の設定
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.6.0</version>
</dependency>
アプリケーションの設定
springdoc:
api-docs:
path: /v3/api-docs
enabled: true
swagger-ui:
path: /swagger-ui.html
enabled: true
operationsSorter: method
tagsSorter: alpha
displayRequestDuration: true
filter: true
packages-to-scan: com.example.controller
paths-to-match: /api/**
OpenAPI の設定
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("My API")
.version("1.0.0")
.description("REST API Documentation")
.contact(new Contact()
.name("API Support")
.email("[email protected]"))
.license(new License()
.name("MIT")
.url("https://opensource.org/licenses/MIT")))
.externalDocs(new ExternalDocumentation()
.description("Wiki Documentation")
.url("https://wiki.example.com"))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.components(new Components()
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("JWT token authentication")));
}
}
コントローラーのドキュメント
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "Users", description = "User management endpoints")
public class UserController {
private final UserService userService;
@Operation(
summary = "Get all users",
description = "Returns a paginated list of all users"
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success",
content = @Content(schema = @Schema(implementation = PageUserResponse.class))),
@ApiResponse(responseCode = "401", description = "Unauthorized",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
})
@GetMapping
public ResponseEntity<Page<UserResponse>> findAll(
@Parameter(description = "Page number (0-indexed)")
@RequestParam(defaultValue = "0") int page,
@Parameter(description = "Page size")
@RequestParam(defaultValue = "10") int size) {
return ResponseEntity.ok(userService.findAll(page, size));
}
@Operation(summary = "Get user by ID")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "User found"),
@ApiResponse(responseCode = "404", description = "User not found")
})
@GetMapping("/{id}")
public ResponseEntity<UserResponse> findById(
@Parameter(description = "User ID", required = true, example = "1")
@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
@Operation(summary = "Create new user")
@ApiResponses({
@ApiResponse(responseCode = "201", description = "User created"),
@ApiResponse(responseCode = "400", description = "Invalid input")
})
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<UserResponse> create(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
description = "User creation data",
required = true,
content = @Content(schema = @Schema(implementation = CreateUserRequest.class)))
@Valid @RequestBody CreateUserRequest dto) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(userService.create(dto));
}
}
スキーマのドキュメント
@Data
@Schema(description = "User creation request")
public class CreateUserRequest {
@Schema(
description = "User's full name",
example = "John Doe",
minLength = 2,
maxLength = 100,
requiredMode = Schema.RequiredMode.REQUIRED
)
@NotBlank
@Size(min = 2, max = 100)
private String name;
@Schema(
description = "User's email address",
example = "[email protected]",
format = "email",
requiredMode = Schema.RequiredMode.REQUIRED
)
@NotBlank
@Email
private String email;
@Schema(
description = "User's password",
example = "SecurePass123!",
minLength = 8,
requiredMode = Schema.RequiredMode.REQUIRED,
accessMode = Schema.AccessMode.WRITE_ONLY
)
@NotBlank
@Size(min = 8)
private String password;
@Schema(
description = "User's role",
example = "USER",
defaultValue = "USER",
allowableValues = {"ADMIN", "MANAGER", "USER"}
)
private UserRole role;
}
@Data
@Builder
@Schema(description = "User response")
public class UserResponse {
@Schema(description = "User ID", example = "1")
private Long id;
@Schema(description = "User's name", example = "John Doe")
private String name;
@Schema(description = "User's email", example = "[email protected]")
private String email;
@Schema(description = "User's role", example = "USER")
private UserRole role;
@Schema(description = "Account status", example = "ACTIVE")
private UserStatus status;
@Schema(description = "Creation timestamp", example = "2024-01-15T10:30:00")
private LocalDateTime createdAt;
}
Enum のドキュメント
@Schema(description = "User roles")
public enum UserRole {
@Schema(description = "System administrator with full access")
ADMIN,
@Schema(description = "Department manager")
MANAGER,
@Schema(description = "Regular user")
USER
}
セキュリティのドキュメント
// Mark endpoint as public (no auth required)
@Operation(summary = "Login", security = {})
@PostMapping("/auth/login")
public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest request) {
return ResponseEntity.ok(authService.login(request));
}
// Require specific roles in documentation
@Operation(
summary = "Delete user",
security = @SecurityRequirement(name = "bearerAuth")
)
@PreAuthorize("hasRole('ADMIN')")
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
API をタグでグループ化する
@Configuration
public class OpenApiConfig {
@Bean
public GroupedOpenApi publicApi() {
return GroupedOpenApi.builder()
.group("public")
.pathsToMatch("/api/v1/public/**")
.build();
}
@Bean
public GroupedOpenApi adminApi() {
return GroupedOpenApi.builder()
.group("admin")
.pathsToMatch("/api/v1/admin/**")
.build();
}
}
エンドポイントを非表示にする
@Hidden // Hide entire controller
@RestController
public class InternalController { }
// Hide specific endpoint
@Operation(hidden = true)
@GetMapping("/internal")
public void internal() { }
主なアノテーション
| アノテーション | 目的 |
|---|---|
@Tag | エンドポイントをグループ化する |
@Operation | エンドポイントを説明する |
@Parameter | パス/クエリパラメーターをドキュメント化する |
@Schema | モデル/フィールドをドキュメント化する |
@ApiResponse | レスポンスをドキュメント化する |
@Hidden | ドキュメントから非表示にする |
@SecurityRequirement | セキュリティスキーム |
このスキルを使用しない場合
- 一般的な OpenAPI 仕様の作成(
openapiスキルを使用) - GraphQL API のドキュメント作成(
graphqlスキルを使用) - Spring Boot 以外の Java プロジェクト
- フロントエンド向け OpenAPI クライアントの生成(
openapi-codegenスキルを使用) - Node.js/TypeScript API(
openapiまたはフレームワーク固有のスキルを使用)
アンチパターン
| アンチパターン | 問題点 | 解決策 |
|---|---|---|
エンドポイントに @Operation がない | ドキュメントの質が低く、説明もない | すべてのエンドポイントに @Operation を追加する |
@Schema に例がない | API の理解が難しい | すべてのスキーマに例の値を追加する |
@Schema を付けずに DTO を公開する | フィールドの説明が不足する | すべての DTO フィールドを @Schema でドキュメント化する |
| 内部エンドポイントを非表示にしない | 実装の詳細が公開される | 内部エンドポイントには @Hidden を使用する |
| セキュリティ要件がない | 認証要件が不明確になる | 必要な箇所に @SecurityRequirement を追加する |
| エラーレスポンスのドキュメントがない | API コントラクトが不完全になる | すべての 4xx/5xx レスポンスをドキュメント化する |
| エンティティクラスを API モデルとして使用する | データベース構造が漏えいする | 専用の DTO クラスを作成する |
| API 情報をコードにハードコードする | 設定が外部化されない | API メタデータには application.yml を使用する |
@Parameter の説明がない | API の使いやすさが損なわれる | すべてのパス/クエリパラメーターをドキュメント化する |
すぐに試せるトラブルシューティング
| 問題 | 考えられる原因 | 解決策 |
|---|---|---|
| Swagger UI が読み込まれない | springdoc の設定が正しくない | application.yml の springdoc.swagger-ui.path を確認する |
| エンドポイントが表示されない | コントローラーがスキャン対象パスにない | springdoc.packages-to-scan を確認する |
| スキーマにフィールドがない | フィールドが private で、getter がない | getter を追加するか、フィールドに @Schema を使用する |
| UI でセキュリティが機能しない | セキュリティスキームが設定されていない | 設定に @SecurityScheme を追加する |
| UI のベースパスが誤っている | サーバー URL が設定されていない | OpenAPI の設定で servers を設定する |
| 生成されたスキーマに型エラーがある | DTO フィールドの型がサポートされていない | @Schema を使用して形式を指定する |
| 循環参照エラーが発生する | DTO が自己参照している | @Schema(ref = "#/components/schemas/X") を使用する |
| enum の値が不足している | enum がドキュメント化されていない | enum フィールドに @Schema を追加する |
| /v3/api-docs で 404 が発生する | springdoc が有効になっていない | springdoc の依存関係を追加し、api-docs.enabled を確認する |
参考ドキュメント
コメント
Markdown 対応