書籍「Web API の設計」を読んだ
はじめに
私はGo言語を主に使っているバックエンドエンジニアです。
少し前にプライベートAPIを書いていたのですが、API設計に不備があり、手戻りになる事態がありました。APIはこれまで何度も書いていますが、体系的に学ぶことなくここまできてしまったので、改めてAPI設計について学びたいと思い、書籍「Web API の設計」を手に取りました。
この記事では、書籍の紹介と、各部での感想や、学びを書き残したいと思います。
書籍の紹介
この本の著者 ARNAUD LAURET さんは、APIの開発経験が豊富なのはもちろん、API関連のカンファレンスでの登壇も多く経験されている方です。
本書では、そもそも「APIとは何か」といったとても基本的なことから、どのようにユーザーフレンドリーなAPIを作るか、途中で引っかかりやすい罠は何かなどを、わかりやすいイラストとともに丁寧に解説しています。私のようにAPI設計を体系的に学んだことのない人には、とても学びのある一冊なのではと思います。他の書籍で学んだことのある人も、何かしらの気づきがあるのではと思いますので、ぜひ一度読んでみてください。
注意点としては、本書は基本的にRest APIをベースに解説されています。gRPC, GraphQL についても言及はありますが、込み入った説明はされていないので、もしそれらに特化した解説を探している方は別の書籍を探すことをお勧めします。
「Web API の設計」の目次
■第1部 APIデザインの基礎
第1章 APIデザインとは何か
1.1 APIとは何か
1.2 APIの設計はなぜ重要か
1.3 APIの設計を構成する要素
1.4 まとめ
第2章 ユーザーを意識したAPIを設計する
2.1 身近なユーザーインターフェイスを設計するための正しい視点
2.2 ソフトウェアのインターフェイスを設計する
2.3 APIのゴールを洗い出す
2.4 APIの設計時にプロバイダの視点を回避する
2.5 まとめ
第3章 プログラミングインターフェイスを設計する
3.1 REST API
3.2 APIのゴールをREST APIに置き換える
3.3 APIのデータを設計する
3.4 設計上の課題に直面したときは妥協点を探る
3.5 RESTはなぜどのAPIの設計にとっても重要なのか
3.6 まとめ
第4章 API記述フォーマットを使ってAPIを記述する
4.1 API記述フォーマットとは何か
4.2 APIのリソースとアクションをOASで記述する
4.3 OASとJSON Schemaを使ってAPIデータを記述する
4.4 OASを使ってAPIを効率よく記述する
4.5 まとめ
■第2部 ユーザブルなAPIの設計
第5章 単純明快なAPIを設計する
5.1 単純明快な表現を設計する
5.2 単純明快なインタラクションを設計する
5.3 単純明快なフローを設計する
5.4 まとめ
第6章 予測可能なAPIを設計する
6.1 一貫性
6.2 適応性
6.3 発見可能性
6.4 まとめ
第7章 うまく整理された簡潔なAPIを設計する
7.1 APIを整理する
7.2 APIのサイジング
7.3 まとめ
■第3部 コンテキストに応じたAPIデザイン
第8章 セキュアなAPIを設計する
8.1 APIのセキュリティ
8.2 アクセス制御を可能にするためにAPIを分割する
8.3 アクセス制御を念頭に置いて設計する
8.4 センシティブな内容に対処する
8.5 まとめ
第9章 APIの設計を進化させる
9.1 APIの進化を設計する
9.2 APIのバージョニング
9.3 拡張性を念頭に置いてAPIを設計する
9.4 まとめ
第10章 ネットワーク効率のよいAPIを設計する
10.1 ネットワーク通信の懸案事項
10.2 プロトコルレベルでのネットワーク通信の効率化
10.3 設計レベルでのネットワーク通信の効率化
10.4 まとめ
第11章 コンテキストに基づいてAPIを設計する
11.1 ゴールとデータの性質に通信を適応させる
11.2 完全なコンテキストを観察する
11.3 コンテキストに従ってAPIスタイルを選ぶ
11.4 まとめ
第12章 APIを文書化する
12.1 リファレンスマニュアルを作成する
12.2 ユーザーガイドを作成する
12.3 実装者に適切な情報を提供する
12.4 進化と終了を文書化する
12.5 まとめ
第13章 APIを成長させる
13.1 APIのライフサイクル
13.2 APIの設計ガイドラインを作成する
13.3 APIのレビュー
13.4 情報提供と共有
13.5 まとめ
第1部 APIデザインの基礎
第1部では、APIとはそもそも何なのか、どうあるべきなのか、なぜAPI設計が重要なのか、などAPIの基礎の基礎から解説されています。この解説の中で、架空の電子レンジが登場するのですが、API設計が下手だといかにユーザーを混乱させ、愛されない製品が出来上がるのかを理解できます。私が直近で開発していたAPIはWeb APIですが、同じWeb APIに限らず、世にあるさまざまなプロダクトが学びの対象になると気づかされました。以下は引用です。
APIの設計者には、物理的なものであれ、仮想的なものであれ、身近なユーザーインターフェイスの設計から学ぶべき点がいろいろある。身近にある物理的なインターフェイス(ドア、キッチン用品、テレビのリモコンなど)から、身近にある仮想的なインターフェイス(Webサイト、モバイルアプリケーションなど)まで、すべてに共通する設計原則がある。
身の回りにある製品の見方が少しだけ変わった気がします。
また、第1部ではWeb API設計の基本的な流れも説明されています。設計時には、徹底してコンシューマ(APIを使う側)の視点に立つことが求められ、逆にプロバイダ(APIを作る側)の視点は排除しないと、プロバイダ側の事情を押し付けたり、内部情報をさらすこととなり、設計は失敗に終わるとのことです。
具体的な方法としては、下記の項目を自問し、言語化していくことになります。
- 誰が使うのか: APIのユーザー(またはプロファイル)をリストアップする
- 何をできるのか: それらのユーザーが何をできるかをリストアップする
- どのようにできるのか: 「何を」のそれぞれを複数のステップに分解する
- 入力(ソース)は何か: 各ステップに必要なものと、それがどこにあるかをリストアップする(見落としている「誰が」、「何を」、「どのように」を割り出す)
- 出力(用途)は何か: 各ステップで返されるものと、それをどのように使うのかをリストアップする(見落としている「誰が」、「何を」、「どのように」を割り出す)
- ゴールは何か: 「どのように」+「入力」+「出力」をわかりやすい簡潔な表現に言い換える
本書の中では、APIゴールキャンバスという表に上記の情報をまとめ、客観的に見ることで漏れをなくします。下の表は、本書内で紹介されている、ショッピングサイトを例としたときのゴールキャンバスの例の一部です。
APIが何をできるのかがわかりやすくまとめられています。このように作るAPIを言語化して初めて、コンシューマ、プロバイダの双方にとってより良いAPIを構築するスタートラインに立てます。
さらにこの部では、OAS(OpenAPI Specification)についても言及されています。Swagger と呼ばれているものです。OAS は、プログラミング言語に依存しない REST API の記述フォーマットで、Open API Initiative(OAI)が推進しているフォーマットになります。気をつけたいこととしては、APIゴールを言語化しないままこのフォーマットで書き始めることは NG という点です。API が何をするものなのかを明確にしないままインターフェイスの設計に進むのは無謀だと著者は主張しています。
第2部 ユーザブルなAPIの設計
第2部では、ユーザーにとって単純で、わかりやすく、かつ驚かせないような API を作るために具体的に何ができるかが書かれています。私は本を読みながら重要だと思った部分や感想はメモを取るタイプなのですが、その内容をそのまま載せると結構な分量になりますので、紹介されている一部の方法をいくつか抜粋したいと思います。
明確な名前を選ぶ
APIインターフェースに限らず、プログラミングをする上でもとても重要で、当たり前となっている考え方ですね。コードを書く人は皆、コードを書く中で、極力明確に、かつスコープやコンテキストを考慮し、無駄なものを取り除くことで簡潔にする、のようなプロセスを経て、より良い命名を普段から意識されていることかと思います。APIのリクエストやレスポンスのフィールド名も同じで、簡潔かつ明確な命名にすることが求められます。
使いやすいデータ型、フォーマットを選ぶ
以下が具体的な例です。
- Unix タイムスタンプはわかりにくく、
2018-08-22T18:01:00z
などの方がわかりやすい -
"type": 1
よりも"type": "checking"
などの方がわかりやすい - 数値型ではなく文字列型が適したケースがある
- 例えば銀行の口座番号。
0123456
などが口座番号の時、数値型にすると先頭の0
の情報が抜け落ちる。コンシューマが混乱するかもしれない
- 例えば銀行の口座番号。
- 複数のエラーを一度に返す
- サーバー側で複数のエラーがわかっているなら
errors
などのプロパティを用意し、一度に返す - 一つずつ返すと、コンシューマは「対応したのにまたエラーが出た...」となりイラつくかもしれない
- サーバー側で複数のエラーがわかっているなら
一貫性を持たせる
以下が具体的な例です。
- 名前を揃える
- あるAPIでは
accountNumber
, 他のAPIではnumber
とするのではなく、統一する
- あるAPIでは
- 型を揃える
- あるAPIでは
accountNumber
は数値型だが、他のAPIでは文字列型、とするのではなく、統一する
- あるAPIでは
- 自分が開発している API の外の世界との一貫性を持たせる
- 自らが関わる API 内だけでなく、組織レベル、そして外の世界との統一性を持たせる
- IOS などで標準規格があるならそれに合わせる
- 自らが関わる API 内だけでなく、組織レベル、そして外の世界との統一性を持たせる
第3部 コンテキストに応じたAPIデザイン
第3部では、API をもう少し俯瞰的に見てどう改善できるか、コンシューマにすでに使われている API の設計を改善する上で直面する問題にどう立ち向かうか、などを解説しています。
そもそも、API 自体の使いやすさ以前にセキュリティは重要です。いくら便利な API でも、安全でなければ利用できません。ここでは、セキュリティを向上する上で活用できる OAuth2.0 の簡易的な解説や、スコープ(コンシューマが API を通して実現できることの範囲)を分割するやり方などが説明されています。
本書では OAuth2.0 のフローの一つであるインプリシット・フローが登場し、逆にそれ以外については全くでてきません。インプリシット・フローやそれ以外のフロー、またそもそも OAuth とは? などについては、以下の記事が参考になるかと思いますので、もし必要な方は参照して頂ければと思います。
スコープを必要なだけ細分化することで「最小権限の原則」を実現します。
スコープを考える時は、API のゴールごとにリソースを特定し、そのゴールを CRUD の動詞で表現すると自ずとできます。本書では銀行の API を例としており、例えば、「口座のリストを取得する」と「口座を取得する」というゴールを考えると、これらは同じ「口座」をリソースとしています。そしてどちらも読み取りを目的とするため、スコープは account:read
などと表現できそうです。ただ、注意点として、何か重要なアクションが一つのスコープに紛れており、スコープの境界の引き方を見直した方が良い、と言うケースもあります。例えば、先の銀行 API でいうところの「振替を更新する」と「不審な振替を検証する」というゴールを考える時に、どちらも CRUD で考えると transfer:update
とできそうですが、よりセキュアにするには transfer:update
と transfer:validate
に分割する、というのも手、ということのようです。
また、第3部では、いかに破壊的変更に対応するか、という関心が高そうなトピックも解説されています。破壊的変更とは、互換性のない変更、つまりコンシューマがコードを更新しないと問題が起きる変更のことを指します。基本的に API の更新をすべてのコンシューマの更新と同時に行うことは困難かと思いますので、API を改善していく上でとても重要となるトピックかと思います。
破壊的変更の例はたくさんあります。
- プロパティ
- 名前変更
- 移動
- 削除
- 型変更
- レスポンスの必須プロパティをオプショナルに変更
- リクエストのオプショナルプロパティを必須に変更
- フォーマットを変更
- 特性(文字列の長さ、数値の範囲、配列の長さ)の変更
- プロパティの意味を変更
- 列挙に値を追加
- ゴール
- 名前変更
- 削除
逆に安全な変更は以下のようなものかと思います。
- レスポンス
- 新しいプロパティを追加
- リクエスト
- 新しいオプショナルプロパティを追加
- 必須プロパティをオプショナルに変更
- 範囲や文字列の長さ、要素数の数を増やす
API をコンシューマがすでに利用しているとなると、API を進化する上で破壊的変更はどこかで起こり得るかと思います。そのための対処法として、API のバージョニングが紹介されています。
API バージョニングは、破壊的変更を避けられない場合の安全な対処方法です。
ここでよく用いられるのは、セマンティックバージョニングです。これは多くの方がご存知かと思いますが、念の為どんなものかを書くと、[メジャー].[マイナー].[パッチ]の3つから構成されます。各バージョンを上げるのは以下のような場合です。
- メジャー: 新しい必須パラメータを追加するなど、破壊的変更が追加されたとき
- マイナー: REST API で新しい HTTP メソッドやリソースパスを追加するなど、後方互換性のある方法で新しい機能を追加するとき
- パッチ: 後方互換性のあるバグフィックス関連の変更を追加するとき
コンシューマ側で対応が必要になるのは基本的にメジャーバージョンが変わる時ですので、これをコンシューマは一番気にします。
API のバージョニングにはレベルがあり、API、リソース、ゴール・アクション、データ・メッセージの4つが紹介されています。REST API のデフォルトとして推奨されているのは API レベルでのバージョニングだそうです。逆に私はこれ以外でのバージョニングを考えたことがなかったので、勉強にはなりましたが、一般的でもある API レベルを利用することが無難な選択肢かと思います。多少の破壊的変更でレベルを上げないといけないというデメリットはありますが、他のレベルの短所である「一緒に使えるのがどのバージョンかがわかりにくい」を考慮すると、正直なところ最も現実的な選択肢かと考えています。
過去のバージョンのメンテナンスについてですが、いつまでもメンテナンスすることはコストになるので、1年など猶予を告知し、期限を設けてメンテナンスするようにするのが現実的かと思います。また、コンシューマにバージョンアップの対応をしてもらうためには、コンシューマが欲しい機能を一緒に含めるなどの工夫をすると良いです。
次に、ネットワーク効率の良いAPIを設計する方法が説明されています。HTTPヘッダーの Cache-Control
, ETag
, If-None-Match
等を使った基本的なキャッシングや、ページングのやり方と注意点、コンシューマがレスポンスに必要な形式・量などを選べるようにするコンテントネゴシエーションなどが詳しく解説されています。無駄にネットワーク帯域を利用しない API を構築するためのヒントが散りばめられています。
その他 API のコンテキストに沿って、どのようにSSE(Server-Sent Event)、Web Socket、はたまた gRPC や GraphQL を使うか、など幅広く、興味深いテーマが解説されています。残りは本書を見てもらえればと思います。
終わりに
「Web APIの設計」を通じて、API の基礎の基礎から、いかにユーザーフレンドりな API を構築するか、さらに破壊的変更やネットワーク効率など、現実的な問題への対処法など、幅広い知識を得ることができました。ただ、これらは全てコンシューマの視点に立つことが前提なので、まずはこの点を肝に銘じて、必要に応じて再度この書籍を参照し、現実の課題と向き合っていこうと思います。
初心者から経験者まで、何かしら学びのある一冊だと思いますので、興味のある方はぜひ手に取ってみてください。
Discussion