OpenAPI Specification ドリブンな開発事例とそれを支えるツール

これは NTT Communications Advent Calendar 2021 3日目の記事です。

こんにちは、イノベーションセンターの松田 (@take4mats) です。 当社の Smart Data Platform (SDPF) のサービスラインナップの多くは、お客さまがサービスご利用に必要な操作を統一的に行うための Web UI に加え、同等の Web API を提供しています。 API 仕様は Knowledge Center にてサービスごとに一般公開されているのをご存知でしょうか? (Knowledge Center で各サービス内の APIリファレンス のページをご覧ください。例えば こちらのリンク)

f:id:NTTCom:20211130155922p:plain

この一般公開されている API 仕様はサービス開発初期に作成され、開発期間にも重要な役割を果たしています。

本記事では、その中で私が携わったサービスから、 API 仕様ドリブンな開発事例を紹介をします。また、これを後押ししてくれた便利なツールもご紹介します。

OAS と他の REST API 仕様の記述形式

前置きとして OpenAPI Specification (OAS) について少しご紹介します。既に OAS をご存じの方は読み飛ばして下さい。

OAS は REST API を記述、生成、使用、及び視覚化するための機械可読性を持ったインターフェースファイルの仕様です。 Version 2 まで Swagger という名前で知られ、主に SmartBear 社のリードで開発されてきました。 Version 3 からは Linux Foundation 配下のオープンプロジェクトとして、 OpenAPI Initiative がリードしています。執筆時点での最新バージョンは v3.1.0 です。

REST API 仕様の記述形式に OAS を採用することで、以下のようなメリットを享受できます。

  • 書きやすさ: YAML 形式 (あるいは後述のツールによる支援)
  • 読みやすさ: YAML から読みやすい HTML を生成するツールが充実している
  • 記述構文に制約: 仕様がブレにくく、人間同士のコミュニケーションコストも削減される
  • 実装に近い: モックを生成できるツールの存在、 JSON Schema 形式の記述部分がそのままコードに流用できる、等

また、念のための程度の情報ですが、 REST API 仕様の記述形式には OAS 以外にいくつかあります。

  • PowerPoint や Word (!): 一般的なドキュメントを書く際に手を出しがちかも知れませんが、差分管理や親切すぎるオートコレクト(ダブルクオーテーションなど)、記述形式が自由すぎる点など多数の難点があります。これ以上のコメントは差し控えます。
  • Markdown などテキスト形式: バージョン管理はしやすくなりますが、記述形式を制限できないために、仕様がファジーになる点は変わりません。読みやすさも実装との距離感も課題ありです。
  • API Blueprint: OAS 以外にもこういった書きやすく、読みやすく、実行できる形式があります。概ね実現したいことを叶えてくれますが、認知度と周辺ツールの充実度で OAS が秀でていると考えています。

とは言っても「OAS の YAML 書くのつらいんだよね…」という方もいると思いますので、色々と楽できる使い方を書いていきます。

とある SDPF サービスでの開発プロセス事例

ここでは、「 API 仕様ドリブンで開発のプロセスを上手く回せたかな」というケースについて Good とし、そうではない Bad な (そしてありがちな) パターンと対比しながら、フェーズごとに紹介していきます。 最初に絵で比較しておくと、以下の図のとおりです。

f:id:NTTCom:20211130155931p:plain

開発要件策定 〜 API 仕様ラフ案策定

まず何よりも先に提供するべき機能、実現されるべきことを定義していきます。この時点で提供物をリソースとしてモデリングします(手法の詳細は割愛)。 次にこれに対応する形で API 仕様を作っていきますが、いきなり OAS 形式で書くのも大変です。 リソースのパスやメソッドを好きな形で書きながら、リクエスト・レスポンス例を JSON で書いて、イメージを膨らませます。

OAS 形式の仕様策定

いよいよ本格的な API 仕様記述です。ここでいかに品質高い仕様を作るかが全体の品質とリードタイムを左右すると言っても過言ではない、というのが私の考えです。

  • Bad

でも早く実装に進みたいし、 YAML ばかりを書くのもシンドイ。全システム同じメンバーで兼任してるから曖昧な記述でも大丈夫。そんな誘惑に駆られることもあるかも知れません…。

  • Good

私個人の見解ですが、どんな人数規模でも、システム間に REST API があるなら仕様記述は緩めずにしっかりやっておきたいです。 仮にチームが小規模でも(自分1人だけだとしても)、人間はエラーをするもの、忘れるものだということを心に刻みます。 実際の執筆によく使うのが、 Stoplight Studio です。 詳細はツール紹介の中で触れますが、直感的なフォームを埋めていくだけで記述でき、一方で生成された YAML の直接編集も可能な頼れるエディタで、執筆作業を楽にしてくれます。 また、 lint 機能として Spectral も利用し、ヒューマンエラーに神経をすり減らすことなく作業を進められます。 そしてこの YAML はアプリケーションコードと同様に GitHub でバージョン管理します。

各システムの実装

いざ、各システムの実装です。

  • Bad

API 仕様書の品質がイマイチだと、仕様の解釈がブレて実装誤りを生みます。 あるいは、仕様の確認のためのコミュニケーションコストが発生します。 なかなか手を動かすことに集中できなくなりますね…。

  • Good

仕様が上手に運用されていると、その API を提供するバックエンドシステムの実装にも直接的に活かすことが出来ます。 バックエンドの実装言語やフレームワークによっては、 Swagger Codegen などで OAS から自動生成されたコードを流用できるかも知れません。 最低でも、 API リクエストのバリデーション部分に JSON Schema として OAS の一部分を活用できるでしょう。

また、この API を叩いて連携する各システム (フロントエンドや他の連携するシステム) の実装にも、この OAS が役立ちます。 仮にこれらシステムの開発者が別のチーム (更には委託などで社外) だとしても、 OAS のファイルさえ渡せば人と人のコミュニケーションを最低限に抑えることが出来て、全員がより実装に専念できます。

単体試験・結合試験・総合試験

想定した API 仕様通りに作ったことを確認し、実際にシステム間をつないで想定通りに動くことを確認するフェーズですね。

  • Bad

API 仕様の運用がイマイチだと、実装誤りに単体試験の段階で気づくことが出来ません。 いざシステム間を結合試験して上手く行かない、さあどっちが悪い!?みたいな戦いに時間を費やすことになるでしょう…。

  • Good

単体試験には Stoplight Prism が生成する API クライアント向けのモックサーバーが役立ちます。これによって API クライアント (連携システム) 側では、自身のリクエスト・レスポンスハンドリングを仕様に則って確認することが出来ます。 これによって単体試験の時点で各システムの API 仕様準拠のレベルを上げることが出来て、複数チームの時間を同時に拘束する必要のある結合試験・総合試験をスムーズに進められるようになります。

リリース

開発・試験が完了すると、デプロイして運用フェーズを迎えますが、外部に公開するAPIの場合はそのドキュメントの作成も書かせません。

  • Bad

公開用ドキュメントをリリース直前に慌てて執筆することになるかも知れません。そしてかなり時間を食うことになるでしょう。また、公開したドキュメントと実装の齟齬が発覚して、ドキュメントあるいは実装の修正なんてこともあるかもしれません…。

  • Good

API 仕様を初期から OAS 形式で記述していると、一般公開のための執筆時間がほぼゼロに出来ます。 OAS の YAML から仕様ドキュメントをホスティングしてくれる有償・無償のサービスを利用したり、あるいはツールを使って YAML から HTML を自動生成し、自前のドキュメントサイトに組み込む事ができます。 今回のケースでは統合されたドキュメントサイトである Knowledge Center に組み込むため、 ReDoc を使った後者の方法を採用しました。

全体

一見面倒そうな OAS での運用ですが、ツールを上手く使うって効率化しつつ、乗り越えていくだけの価値があると考えています。 Bad パターンは色々と極端に思われる方もいるかも知れませんが、こういった幾多の手戻り・ムダを排除し、品質の向上と、開発時間の短縮に大きく貢献するはずです。 私自身もこの経験だけで完璧だとは思っていないので、細かい課題や別のプロジェクトに合わせた改善を重ねていきたいです。

ツール紹介

ここでは、開発プロセス事例で触れたツールを紹介していきます。 Stoplight や ReDoc はとても良いツールだと思うのですが、なかなか日本語の情報がなく、役に立てばという思いもあって記事にしました。 先に API Spec とシステムとツールの関係性を絵で紹介すると下図のようになります。

f:id:NTTCom:20211130155939p:plain

エディタ: Stoplight Studio

執筆用には2つのビューを持っていて、 Form view では直感的なフォームを埋めていくだけで記述していけるので、 OAS 形式の YAML の構文に自信がなくても記述していくことが出来ます。 裏では実際に OAS 形式の YAML をリアルタイムに生成していて、 Code view でそれを直接編集できます。 request/response のサンプルを複数書く方法のように、 Stoplight Studio を使って初めて理解できた記法もありました。 また、 body の定義の漏れや手間を圧倒的に解消してくれたのもありがたいところです。 ちなみに私の使い方ですが、 Code view の VS Code ライクなコードエディタ画面で直接ざっと書いてから、細かい部分 (request/response body の examplescomponents/schemas 配下の スキーマなど) の記述に Form view を使う方法が好みです。

編集画面は図のとおりです。左にファイルや API パス、中央が編集画面(下部が example response を定義する場面)、右に Spectral による lint の結果がリアルタイムで表示されています。

f:id:NTTCom:20211130155947p:plain

私は公式サイトから Mac 版 Desktop App をインストールして使用していますが、 Windows 版やブラウザ版もあります。

Linter: Stoplight Spectral

VS Code エクステンションの openapi-lint などで事足りるのですが、 Stoplight 製品群に合わせる目的で Spectral を使うようにしています。 前述の通り、 Studio を使っていれば同梱されていて自動で使うことになります。 GitHub Actions に組み込む自動チェックにもこれを用いています。

NPM でインストール可能なので、 CLI でも javascript の中でも利用可能です。

# インストール
npm install -g @stoplight/spectral-cli

# OAS 標準ルールだけを使用
echo 'extends: "spectral:oas"' > .spectral.yml

# 検査対象のファイルを指定して実行
spectral lint ./path/to/spec.yaml

独自の拡張ルールも定義できるので、こだわりのある人には更に価値がありそうです。

モック: Stoplight Prism

Prism はインストールも簡単で複数の役割をこなせる、とても手軽で便利なプロダクトです。

  • YAML ファイルを食わせて Docker コンテナとして稼働できるので、どこでも気軽に立ち上げることが出来ます。
  • Mock モード: クライアント開発用に API サーバーをモックしてくれる
    • 正常リクエストへの応答: API 仕様に沿う形で header/body を自動生成してレスポンス
    • 仕様非準拠のリクエストへの応答: 仕様のどこに準拠していないのか、 body で説明してくれる
  • Validation Proxy モード: Prism には Validation Proxy というモードも有り、 API サーバーのレスポンスの正当性を検証することも可能です。

Mock と Validation Proxy 、2つのモードを図解するとこのようになります。

f:id:NTTCom:20211130155959p:plain

Mock を実際に動かしてみます。 Spec file は有名な PetStore を利用します。 まずはじめに GET の成功例です。 左が Mock 起動とモニタリングログの様子、右が API client としての HTTPie コマンドの様子です。 無事に 200 応答を受けています。

f:id:NTTCom:20211130160007p:plain

続いて、 POST に失敗した時の様子です。画面右で、 body に必要な name を抜いた状態で request し、 response 422 が返ってきています。 sl-violation というヘッダに仕様違反の内容が記載されています。

f:id:NTTCom:20211130160014p:plain

他にも、 Prefer header を付与してレスポンスを指定の example にしたり、 YAML に x-faker extension で追記することで Faker.js を使ったリアルな値を自動生成するなど、凝った使い方もできます。 ここまでの機能が欲しくなるかはわかりませんが、気になる方は 公式 doc をご参照下さい。

HTML の自動生成: ReDoc

作った API 仕様書は自社の Knowledge Center という Web サイトに統合する必要があり、その観点で ReDoc に行き着きました。

ReDoc は静的 HTML を生成してくれるためポータビリティがあり、かつ UI はリッチです。 リリースのサイクルとしては、コードをプッシュ&マージしたら自動生成・パブリッシュできるように GitOps を組んでいます。

利用方法は簡単で、以下のようなコマンドで Docker で起動できます(CLI ツールである redoc-cli を NPM でインストールして使うことも出来ます)

docker run -p 8080:80 -e SPEC_URL=https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore-expanded.yaml redocly/redoc

これで localhost:8080 にアクセスするとご覧のとおりです。

f:id:NTTCom:20211130160023p:plain

Stoplight ファミリーはと言いますと、選定当時は Hosted Docs (Stoplight のサイトで公開される) しか提供されていませんでしたが、最近は Elements という名称で Web コンポーネントとして無償提供されているようです。 自前の Web App にも簡単に埋め込めるし Spec file の実体を Stoplight に預けるわけでもないため、これは良いかも知れません…。

おわりに

OAS ドリブンな開発事例と、それに関わるツール紹介というちょっとニッチな?話をしました。 よりよく使ってもらえるプロダクトやアプリケーションを作ることが第一ですが、そのためのプロセスや道具も磨いていきたいものです。

また、今回の開発プロセスにはまだ改善点がありますし、ツール類も日々進化していきますので、しばらくすると自分の中でもベストプラクティスが変わっていきそうです。 この記事が参考になれば幸いですが、お読みいただいた皆さんもぜひ、ご自身のやり方を教えて下さい。

それでは、明日の記事もお楽しみに!

© NTT Communications Corporation All Rights Reserved.