Swagger UIを入れてみたものの、実装を直したのにドキュメントが更新されていなかった、という経験はありませんか。APIドキュメントの自動生成手段には大きく分けて Spring REST DocsSpringdoc OpenAPI の2つがあり、それぞれ得意分野が違います。本記事では両者の仕組みを整理して、どちらを選ぶべきかの判断軸を提示します。

2つの生成方式の違い

まず根本的な仕組みを押さえましょう。

Spring REST Docs はテスト駆動です。MockMvcやWebTestClient、REST Assuredで書いたテストの実行結果から、リクエスト・レスポンスのスニペット(Asciidoc断片)を生成します。テストが落ちればビルドも落ち、ドキュメントも更新されません。

Springdoc OpenAPI はアノテーション駆動です。実行時にコントローラのアノテーションとメソッドシグネチャをリフレクションで解析し、OpenAPI 3仕様のJSONを生成します。Swagger UIから対話的に叩けるのが特徴ですね。

なお、かつて主流だったSpringFoxはSpring Boot 3系に対応せず実質的にメンテナンス終了しています。新規プロジェクトの選択肢からは外して考えて問題ありません。

ドキュメントと実装の乖離リスク

どちらを選ぶかで一番悩むポイントが、ここだと思います。

REST Docsは「テストで叩いた実物」をそのままドキュメント化するので、嘘が書けません。レスポンスのフィールドを1つ増やしたのにドキュメントに反映し忘れる、ということが起こりにくい構造です。フィールド定義に漏れがあればdocument()がテストを失敗させてくれます。

一方Springdocは便利な反面、@Schemaの付け忘れや@Operationの説明が古いまま、といった乖離は実行時まで検知できません。テストとは独立して動くので、ドキュメントの正確性はレビュアーの目視に頼ることになります。

ただ、テスト文化がまだ育っていないチームでは、REST Docsの「テスト書かないと何も出ない」という性質はハードルになります。即効性ではSpringdocが圧勝です。

Spring REST Docsの最小構成

実際のコードを見てみましょう。Gradleの設定はこんな感じです。

plugins {
  id 'org.asciidoctor.jvm.convert' version '3.3.2'
}

ext {
  snippetsDir = file('build/generated-snippets')
}

dependencies {
  testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
  asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
}

test {
  outputs.dir snippetsDir
}

asciidoctor {
  inputs.dir snippetsDir
  dependsOn test
}

テスト側はMockMvcにdocument()を挟むだけ。

@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
class UserControllerDocsTest {

  @Autowired MockMvc mockMvc;

  @Test
  void getUser() throws Exception {
    mockMvc.perform(get("/users/{id}", 1))
      .andExpect(status().isOk())
      .andDo(document("users/get",
        pathParameters(
          parameterWithName("id").description("ユーザーID")
        ),
        responseFields(
          fieldWithPath("id").description("ID"),
          fieldWithPath("name").description("氏名")
        )
      ));
  }
}

src/docs/asciidoc/index.adocからスニペットをinclude::で取り込めば、./gradlew asciidoctorでHTMLが生成されます。MockMvcの基礎が不安な方は Spring BootのMockMvcでコントローラをテストする もあわせてどうぞ。

Springdoc OpenAPIの最小構成

こちらは依存を1つ足すだけです。

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

これで/swagger-ui.htmlがもう動きます。アノテーションで情報を補強しましょう。

@RestController
@RequestMapping("/users")
@Tag(name = "User", description = "ユーザー管理API")
public class UserController {

  @Operation(summary = "ユーザー取得")
  @GetMapping("/{id}")
  public UserResponse get(
    @Parameter(description = "ユーザーID") @PathVariable Long id
  ) {
    return service.find(id);
  }
}

グローバル設定はBeanで定義します。

@Bean
public OpenAPI customOpenAPI() {
  return new OpenAPI()
    .info(new Info().title("User API").version("v1"))
    .components(new Components().addSecuritySchemes("bearer",
      new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer")));
}

Swagger UI導入の基本は Spring BootでOpenAPI/Swagger UIを使う で詳しく扱っています。

比較表で整理

判断に使える観点で並べてみます。

観点Spring REST DocsSpringdoc OpenAPI
生成タイミングビルド時(テスト実行時)実行時(リフレクション)
実装との一致強い(テスト連動)弱い(アノテーション任せ)
初期導入コスト中(テスト前提)低(依存追加のみ)
学習コストやや高い低い
出力形式Asciidoc/HTMLOpenAPI仕様/Swagger UI
Try it outなし(静的HTML)あり
OpenAPI仕様出力標準では不可(拡張で可能)標準
CI連携テストが壊れたら自動検知別途検知の仕組みが必要
公開向き静的ドキュメントとして配布開発者ポータル・SDK生成

用途別の選び方

外部公開API・SDK配布が必要なら OpenAPI仕様がほぼ必須なので、Springdoc中心、または後述の併用パターンが現実的です。クライアントコード自動生成やAPI Gateway連携も仕様ベースで動きます。

金融・医療など厳密な仕様遵守が求められる社内API はREST Docsが向きます。「ドキュメントに書いてあることはテストで保証されている」という強い性質が効きます。

マイクロサービスで開発者ポータルを整備したい ケースは、各サービスがOpenAPI仕様を出す前提でSpringdocが扱いやすいです。BackstageなどのIDPとも相性が良いですね。

テスト文化がまだ薄い小規模チーム は、まずSpringdocで動くものを出して、必要になったら段階的にREST Docsへ寄せる流れがおすすめです。

併用パターン - restdocs-api-spec

「REST Docsの正確性が欲しいけどOpenAPI仕様も配りたい」という贅沢な要求には、 restdocs-api-spec という選択肢があります。epages-deがメンテしているサードパーティ拡張で、REST Docsのテストから直接OpenAPI 3.0仕様を吐き出せます。

plugins {
  id 'com.epages.restdocs-api-spec' version '0.19.4'
}

dependencies {
  testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.19.4'
}

openapi3 {
  title = 'User API'
  version = '1.0.0'
  format = 'yaml'
  outputDirectory = 'build/api-spec'
}

./gradlew openapi3openapi.yamlが出力されるので、それをSwagger UIやRedocに食わせれば対話的なドキュメントになります。テストが品質を担保し、出力はOpenAPIという、いいとこ取りの構成です。

ただし学習コストはそれなりにかかります。チームにREST Docsを書ける人が複数いることが前提と考えたほうが良いです。

落とし穴とCIでの注意点

いくつか実プロジェクトで踏みやすい問題を挙げておきます。

REST Docsで意外と多いのが、ローカルで./gradlew asciidoctorだけ実行してテストをスキップしてしまうケース。古いスニペットでHTMLが組み上がってしまうので、CIでは必ずtestを先に走らせましょう。asciidoctorタスクにdependsOn testを入れておくと安全です。

SpringdocはSpring Securityとの組み合わせで/swagger-ui.htmlが401になりがちです。SecurityFilterChain/swagger-ui/**/v3/api-docs/**を明示的に許可する必要があります。本番では逆にこれらを閉じる設定も忘れずに。

リフレクションを多用するので、ネイティブイメージ(GraalVM)やプロキシ環境ではスキーマ生成に失敗することがあります。導入前に対象環境で一度ビルドを試しておくと安心です。

まとめ

選択の判断軸はシンプルです。「テスト文化があるか」と「外部公開するか」の2軸で考えれば、だいたい決まります。

  • テスト文化あり × 社内API → REST Docs
  • テスト文化あり × 外部公開 → restdocs-api-spec併用
  • テスト文化薄い × どちらも → Springdocで開始

迷ったらSpringdocで小さく始めて、乖離が実害になってきたタイミングでREST Docsを部分導入する、というのが現実的な進め方だと思います。まずは CRUDチュートリアル で作ったような小さなAPIに両方試してみて、肌に合うほうを選んでみてください。