『Web APIの設計』を読んだ

3連休初日。めちゃくちゃ寒いし緊急事態宣言なので、引きこもって本を読んだ。 2020年は、技術書を読んでもブログに書くのをサボっていた。書く時間がムダという説もあるが、すぐに利用する領域でもないかぎり、読んだら次の日にはすべて忘れている。 なんらかアウトプットしておくと少なくともあとで見返せるし多少は整理できるので、久しぶりにメモをとりながら読んだ。ので、ブログに残す。

www.shoeisha.co.jp

社内向けの単機能なAPIとか、特定の外部サービスから叩く用のWebフックAPIはつくったことあるけど、もう少し広めのドメインかつ実環境に公開するWeb APIを0からつくったことがない。 そのため全体像がわかるような本はないかと思っていたところ、本書をみつけた。

感想

タイトルに偽りなく、Web APIの設計に関して包括的な情報が得られるようになっている。 想定したよりも既知の内容だったので自分の知識が致命的なほどダメでないことが確認できて安心したし、逆に知らなかった情報(プロトコルの現状やOAS周りなど)も得られて、未知と既知のバランスがちょうどよかった。

全体の構成としては、銀行の口座情報を管理するAPIを題材として、各章のテーマに添わせて実例を示している。 ソフトウェアあるいはユーザインターフェイス一般にいえる原則的な考え方から実際に使えるツールまで、Web APIの設計に関わる流れを一気に説明している。

また、以下の記述で端的に示されているように、理想的な設計を紹介しつつ、現実問題も適度に混ぜ合わせながら実用的な流れが説明されている点もよかった。

それはよいとして、この話が通用するのは理想の世界だけである。そして、ここは理想の世界ではない。(第9章より)

ただ、例および日本語での記述がわかりづらい部分もあった。これは、自分の中でわりと既知の内容っぽい箇所は例示の記述を飛ばしたことも多分に影響していると思われる。各章は独立しているほうが好みなので、今後の本選びの参考にしたい。

読んだあとにググってみたところ、どうやらWeb API設計界ではオライリーの以下本がバイブル的な位置づけっぽい。

www.oreilly.co.jp

目次を見たかぎりカバーする領域はほぼ同じようにみえる。いつか読んで比べてみたい、そんな気がする。

メモ

読みながら書いたメモを残す。引用ではない記述も多々あるので、信用しないように。

第1部 APIデザインの基礎

第1章 APIデザインとは何か

  • APIとは、2つのシステム、対象者、組織などが出会い、やり取りするポイントである
  • APIの目的は、人々がそれぞれの目標をできるだけ簡単に達成できるようにすることである

第2章 ユーザーを意識したAPIを設計する

  • ユーザが何をできるかに焦点を当てると、インターフェイスはシンプルになる
  • 組織・データ・コードの構造は実装に閉じ込める

第3章 プログラミングインターフェイスを設計する

  • HTTPは、WWWの通信の基盤となるプロトコルであり、リソースをやりとりする
  • REST APIは、HTTPプロトコルを利用してリソースをやりとりするAPIである
  • 分散システムに要求される性質として、効率性、拡張性、信頼性、再利用性がある
  • これらの性質をみたす状態をRESTfulといい、RESTfulなAPIをREST APIと呼ぶ
  • RESTfulであるためのアーキテクチャをRESTアーキテクチャスタイルと呼ぶ
  • RESTアーキテクチャスタイルは、クライアント・サーバ分離、ステートレス性、キャッシュ可能性、階層化システム、コードオンデマンド、統一インターフェイスの6つの制約に従う
  • API設計はユーザフレンドリ性と設計原則との妥協を探る必要がある

第4章 API記述ドキュメントを使ってAPIを記述する

  • OpenAPI Specification(OAS)は、REST API記述フォーマットである
  • OASドキュメントの記述には、対応したエディタを使うのがオススメである
  • OASにおいて再利用可能なコンポーネントを利用できる

第2部 ユーザブルなAPIの設計

第5章 単純明快なAPIを設計する

  • 単純明快な設計にする
  • ユーザがすぐに使える(意味のある)データを提供する
  • APIには、不正な形式のリクエストエラー、機能的エラー、サーバーエラーの3種類のエラーがある
  • 人々は、設計が全体的に統一されていることを求める(例:日付のフォーマット)
  • 命名・データ構造は、一度決めたら最後まで貫くべきである

第6章 予測可能なAPIを設計する

  • APIの振る舞いはAPIのゴールによって決定されるため、一貫性のあるゴールを設定するべきである
  • APIは、内部・組織・問題領域・そして世の中一般に対して一貫性を保つべきである
  • 世間一般の一貫性を保つには、標準や一般的な設計に従うことが効果的である
  • APIの適応性を保つため、HTTPであることを利用してコンテントネゴシエーションを利用するべきである
  • コンテントネゴシエーションは、単一のリソースに対してHTTPヘッダで表現形式を指定できる
  • コンテントネゴシエーションのヘッダにはContent-Typeレスポンスヘッダ、Acceptリクエストヘッダなどがある
  • Rangeヘッダはバイナリの一部を取得するために定義されているが、itemsのようなカスタム単位を用いてページングに利用することもできる

第7章 うまく整理された簡潔なAPIを設計する

  • リクエスト・レスポンスのデータ構造を整理する
  • APIの入力および出力のプロパティの数・深さは、その数値が問題なのではなく、機能的に妥当かどうかが焦点になる
  • 数は20、深さは3を超えないことを筆者が推奨している
  • APIを整理した結果、別のAPIに分割することもありうる

第3部 コンテキストに応じたAPIデザイン

第8章 セキュアなAPIを設計する

  • OAuth 2.0は認可フレームワークである
  • OpenID ConnectはOAuth 2.0に基づく認証プロトコルである
  • APIではアクセス制御だけでなく、そのデータを公開するべきかを考える必要がある
  • OAS 3.0ではスコープの記述をサポートしている
  • 公開するデータは必要最小限にする
  • 何をセンシティブなデータとみなすかは明確でない場合が多いため、他分野の専門家に相談する

第9章 APIの設計を進化させる

  • ベースとなるプロトコルを理解し、どのくらい信頼できるのかを把握する
  • 出力データを変更するもっとも安全な方法は、純粋かつ単純に新しい要素を追加することである
  • HTTPクライアントは、1桁目以降が未知のステータスコードをx00として解釈する(RFC7231)
  • APIに十分な数のユーザがいるとき、明示した仕様以外も誰かに依存する(ハイラムの法則)
  • APIのバージョニングはコントラクトに関する変更のみ新見があるため、セマンティックバージョニングにおけるパッチバージョンは存在しない
  • パスとドメインにバージョン番号を含める方法がもっとも一般的に使われている
  • クエリパラメータやContent-Typeを用いた方法もあるが、あまり使われない
  • REST APIでは、APIレベルのバージョニング以外に、リソースレベル、ゴールレベル、データ/メッセージレベルのバージョニングが可能である
  • ただし、結局は他のリソースやゴールとの互換性を考慮する必要があるため、推奨はされない
  • APIの破壊的変更はコンシューマにとって嬉しくはないため、嬉しい機能の追加とあわせるべきである
  • 破壊的変更のリスクをへらすためには、拡張性の高いAPIを設計することが重要である
  • レスポンスオブジェクトに関して、プロパティの型を慎重に選択し、自己記述的なデータにするべきである
  • 自分が行うことは保守的に、他人から受け取るものには寛容に。(ポステルの法則 - 堅牢性原則)
  • APIにあてはめると、レスポンスに一貫性をもたせ、エラーを包括的に処理する
  • APIが大きくなるとは快適変更のリスクが高まるため、適切なグループに分けて小さく分割すべきである

第10章 ネットワーク効率のよいAPIを設計する

  • APIの設計者は、ネットワーウ通信の課題を理解していなければならない
  • APIのネットワーク効率最適化は、まずプロトコルレベルで行うべきである
  • プロトコルレベルの最適化
    • データ圧縮、持続的接続、キャッシュ、条件付きリクエストがある
    • HTTP/2は、持続的接続、同時リクエスト、バイナリトランスポートをデフォルトでサポートしている
    • HTTPのキャッシュ機構
      • クライアントは、Cache-Controlヘッダで指定された期間を超えない場合、キャッシュを参照する
      • 超える場合、サーバに問い合わせる
      • E-Tagの一致するデータが更新されていない場合、キャッシュの有効期限を更新し、そのキャッシュを参照する(条件付きリクエスト)
      • 更新されている場合、サーバからデータを取得してE-Tagの値を更新し、キャッシュする
    • 更新されたかだけを知りたい場合は、GETではなくHEADを使えば良い
    • HTTPのキャッシュ機構を、通信フレームワークが常に利用できるとも限らない
      • gRPCはサポートしていない?
    • コンシューマに対してデータの有効な期限を伝えるため、TTLをデータとして返す場合もある
  • 設計レベルの最適化
    • データをフィルタする、必要なプロパティに絞ることで通信量を削減する
    • データを集約することでAPI呼び出し回数を削減する
    • ただし、(キャッシュを含めた)データのライフサイクルを意識するべきである
    • 柔軟な集約
      • コンテントネゴシエーションで取得するデータの詳細度を指定する
      • クエリパラメータでプロパティを指定する
        • GraphQL
  • ネットワーク効率が悪いということは、APIの設計がユーザのニーズを満たせていない兆候かもしれない

第11章 コンテキストに基づいてAPIを設計する

  • 処理に時間がかかる場合は、202を返し、非同期に処理する
    • ステータスを取得するゴールを提供する(ポーリング)
    • プロバイダからコンシューマのAPIを介して処理の完了を通知する(Webフック)
  • SSE(Server-Sent Event)
    • 株価のような変動的なデータをストリームとして取得するのに用いるデータフォーマットである
    • HTTPプロトコルをベースした、HTML5用の規格である
  • 双方向のイベント送信には、WebSocket(RFC6455)が用いられる
  • APIの設計においては、プロバイダだけでなくコンシューマの機能的・技術的制限を特定し、慎重に検討することが必要である
  • Web APIを簡単に要約すると、単体の同期型のリクエスト/レスポンス+REST+HTTP/1.1+JSON Web APIになる
  • APIスタイルは、コンテキストとニーズを分析して選択する
    • RESTはリソース指向であり、HTTPプロトコルを活用する
    • gRPCは関数指向であり、トランスポート層としてHTTPを活用する(セマンティクスは使わない)
    • GraphQLはデータ指向であり、プロトコルに依存しない(通常はHTTPが採用される)

第12章 APIを文書化する

  • ドキュメント作成も標準に従うほうがよく、ReDocというツールが有用である
  • リファレンスマニュアルは、基本的に設計時のドキュメントを参考にできるはずである
  • ただし、そのまま流用するのはプロバイダの視点が入り込まないよう、注意深く検討するべきである
  • 利用者に対して、ユースケースやセキュリティに関して記述したユーザガイドが必要である
  • 実装者に対して、データマッピングやエラーマッピングに関して記述したプロバイダ視点のドキュメントが必要である
  • 破壊的変更のあるなしに関わらず、変更はドキュメント化されるべきである
  • OASにおいて、パラメータ、ゴール、プロパティに対してdeprecatedフラグを適用できる

第13章 APIを成長させる

  • APIの一貫性は重要であり、そのために設計ガイドラインを定義すべきである
    • リファレンスガイドラインは、HTTPメソッドやヘッダ、コードなどをどの状況で利用すべきかや、共通のIDの定義などを説明する
    • ユースケースガイドラインは、APIの利用に関してどういう使い方がなされるべきかを説明する
    • 設計プロセスガイドラインは、設計者が把握すべき情報へのリンクなどを説明する
    • 最初は小さく正確に構築し、単独ではなく共同で、着実にメンテナンスしていく
  • APIの設計者には、自分の設計をあらゆる面から疑い、正当性を問い、調査・検証・分析を行う責任がある
  • 何よりもまず、ニーズの検証と分析を行わなければならない
  • 設計のレビューは、プロバイダとコンシューマそれぞれの視点でレビューする
  • 設計者は、自分の設計内容を共有し、コンシューマにレビューしてもらう必要がある