Swaggerを分割して管理しやすくする

Swaggerを導入したきっかけ

弊社では、2018年頃から「WebのサーバーサイドはAPIとして機能提供」スタイルが中心となってきました。

API開発ではページ組み込み系と比べ、データのIN/OUT設計をよりしっかりと行う必要があります。APIという事で、IN/OUTが剥き出し状態になるので、当然といえば当然です。

それほど重要なAPIのIN/OUT定義、仕様設計については、ドキュメント形式について色々と悶着がありました。
結果、弊社では「Swagger」(またはOpenAPI)をメイン仕様書として使うスタイルで落ち着いています。

Swaggerとはなんぞや?というお話については、割愛しますので、以下の詳しく紹介してくれている記事などをご参照ください
【連載】Swagger入門 – 初めてのAPI仕様管理講座 [1] Swaggerとは|開発ソフトウェア|IT製品の事例・解説記事 

Swaggerを運用してわかった良かったこと・悪かったこと

Swagger、本当に便利ですね!大変快適です。
Excelで管理しようとしていた時代から比べると開発がしやすくて仕方ありません。

とはいえ悪かったこともあります。Swaggerを仕様書のメインに据えてAPI開発を行って感じた、良かった・悪かったをまとめます。

良かったこと

  • エンドポイントのIN/OUT設計書を短時間で記述できる
  • 設計書がSwagger YAML形式のフォーマットで統一されるので、誰が設計書を書いても同じフォーマットになる
  • チームのSwagger理解レベルが一定水準になると、仕様すりあわせにかかる時間が減った(ドキュメント見ればわかる状態)
  • Swaggerファイルを共有するだけで、成果物と仕様の乖離に大きなズレが発生することが少なくなった

悪かったこと

  • 1枚のYAMLファイルで管理をしていると、複数人で併行編集作業がしづらい。というよりもほぼできない(Gitでマージする際にコンフリクトが確実に発生する)
  • エンドポイント数が20〜30を超えてくると、ファイルが巨大化してメンテナンスが大変になってくる
  • 巨大化したSwaggerがコンフリクトを起こすと、マージ作業がかなり厄介になる

ファイルが巨大化する問題と、併行作業出来ない問題については、運用フローでカバーしようという試みも行いました。が、それでも問題点としてチームの議題にあがることが多々ありました。

Swaggerを分割管理してメンテナンスをしやすく

そもそも1ファイルに対して、併行作業するなよって話ですが…。

チーム全体のSwagger理解を深めるためにも、特定の誰かがSwaggerをメンテナンスするというフローよりは、併行作業をしやすくする方向で解決方法がないか?を探しました。

結果「Swaggerファイルの分割をしてみよう」という結論になりました。

Swaggerの分割を実践

1. やり方について

手段としては、いろいろツールがあります。
が、やるべきことはシンプルに以下2点です。

  • YAMLとして成立する単位でSwaggerの記述内容を別ファイル化
  • 複数のファイルに分割したYAMLファイルを1枚のSwaggerファイルにビルド

極論をいうと、複数のYAMLファイルから1枚のSwagger YAML形式のファイルをビルドできればなんでも良さそうです。 正直、作れる人はお手製のシェルでもなんでもいいと思います。

私は、参考にした記事をお手本に、以下のnpmライブラリを使って、YAMLから他のYAMLファイルを読み込むように設定しました。

  • json-refs

このライブラリとgulpファイルを使い、変更を検知して自動ビルドを行うように設定します。

2. 自動ビルドの設定

パッケージ構成1

「/src」以下が、分割したファイル群で、ビルドされたSwaggerファイルがルートディレクトリに出力されるという構成です。

  • index.yaml
    • 読み込み開始の起点となるYAMLファイル
  • _paths.yaml
    • pathsフォルダ以下にあるYAMLファイルの読み込み設定を記述したファイル
  • _components
    • componentsフォルダ以下にあるYAMLファイルの読み込み設定を記述したファイル
  • paths
    • エンドポイント定義については、paths以下に外部ファイル化
  •  components
    • 共通のレスポンスは使い回せるようにcomponentsフォルダ以下に外部ファイル化

ビルド設定については、前述の通りgulpfile.jsに全て行います。

packege.json

gulpfile.js

index.yaml

_paths.yaml

_components.yaml

paths以下のファイルは、エンドポイントのスキーマ定義を別ファイル化しただけの内容です。こんなざっくりしたAPIのエンドポイントはありえない話ですが、サンプルということで。

/paths/*.yaml

components以下のファイルについては、共通レスポンスの定義部分を別ファイル化しているだけの内用です。こんなにざっくりしたレスポンスはありえないですが、サンプルということで。

3. ビルドの実行

パッケージ直下で、以下のコマンドを実行すれば、src以下の変更を検知してSwaggerファイルを自動ビルドします。

yarnを使っている場合

4. ブラウザ確認環境の設定

ここまでの設定で、Swaggerファイルは自動ビルドされるようになりましたが、出来上がったSwaggerファイルの確認が少し面倒くさいため、 DockerでSwaggerUIが起動する様に設定します。

パッケージ構成2

docker-compose.yamlを追加。
SwaggerUIの設定を行います。

作成したdocker-composeファイルを使用して、以下コマンドを実行

docker-compose up 

ブラウザで「http://localhost:50000」を開くと作成したSwaggerファイルの確認が可能になります。

SwaggerUI

5. モックAPIサーバーの設定

Swaggerの更新と連動したモックAPIサーバーも設定します。 docker-composeファイルを以下の内容に更新

以下、コマンドを実行

 

「http://localhost:50001」で、Swaggerファイルで定義したAPIエンドポイントのモックサーバーが起動します。

PostmanなどHTTPクライアントでAPIをテスト。

エンドポイントの定義を記述した/usersに対して、GETリクエストを実行します。
Swaggerに記述したexampleの内容でレスポンスが返ってくることを確認。

POSTMAN-OK

x-api-keyで、クライアントとAPIサーバーの認証を行うという想定なので、x-api-keyが送信されなかった時、リクエストエラーになる事を確認。

POSTMAN-NG

ここまでで、一通りのSwagger周りの環境設定が完了しました。
Swagger更新 › ブラウザで確認 › お好みでモックAPIを使用してAPIを実装
という一連のAPI開発サイクルが可能になると思います。

また、ファイルを分割した事により、複数人によるSwaggerの編集作業も少しはしやすくなると思います。

Swaggerを分割しても解決しないこと

とはいえ、Swaggerを分割してもコンフリクトは発生します。
主なコンフリクト発生ポイントは以下となります。

  • 併行作業時に必ずコンフリクトするファイル
    • swagger.yaml
      • merge後にビルドし直す必要がある
        • 既存の手作業コンフリクト解消よりは全然マシかなと思います
  • 併行作業時にコンフリクトしやすいファイル
    • _path.yaml
      • エンドポイントのパス設定が集中するので、コンフリクトが起きやすい
        • 記述内容は、パス設定と読み込みファイル設定だけなので、コンフリクト解消は、かなりライト
    •  paths/*.yaml
      • GETのエンドポイントはAさんで、POSTのエンドポイントはBさんが記述とかすると、1ファイルを2人で編集することになるので、コンフリクトはそりゃ起きますよねって話です
        • Swagger全体でコンフリクトを起こされた時に比べると、コンフリクト解消は、かなりライト

所感としては、コンフリクトが起きはするものの、ファイルを分割したことによりコンフリクト解消の負担はかなり軽減出来ていると思います。

Swaggerを分割することによる問題

Swaggerファイルを分割して、気付いたデメリットは以下です。

  • エラーがわかりにくい
    • 読み込み対象のYAMLファイルパスが間違っている時など
  • SwaggerEditorで最終確認すると、エラーが出ている時がある
    • SwaggerUIではバリデーションが通ってしまうケースがある(型定義のスペルミスなど)
  • Swaggerファイル本体を編集してpushしてしまうケースがある
    • チームメンバーにSwagger分割について認識共有が出来ていない時などが原因

対策としては、編集完了後に必ずブラウザ版のSwaggerEditorを開き、 ビルドしたSwaggerを貼り付けて最終チェックを行うというフローで解決は出来ます。設定で解決は出来そうですが、ひとまず今はこの運用をしています。

まとめ

Swaggerを分割することでSwaggerのメンテナンスが苦痛ではなくなりました。特にエンドポイントをファイル単位で管理できる所が非常に◎でした。

多少の面倒くささは残りますが、10,000行を超えるSwaggerファイルのコンフリクトを直していた時の事を考えると…って感じです。

私は、この状態で過不足ないので、これ以上の工夫はしていません。
が、今ならもっとSwaggerのメンテナンスをお手軽にしてくれる設定などがあると思います。

Swaggerのメンテナンスで苦労をされている方は、Swaggerの分割、もしくはその他の方法を模索してみてはいかがでしょうか?