1 minute read

2020年8月出版の『Web APIの設計』を読み終わったので、自分が印象に残ったところをまとめようと思います。

出版社の紹介ページ


目次


全体の内容と感想

この本はフランス人システムアーキテクトのアルノー・ロレの『The Design of Web APIs』の邦訳です。

Web API、もっと言えば REST について、基本的なことからセキュリティやネットワーク、ドキュメントに関わることまで、知っておくべきことを広く丁寧に説明している本でした。

RESTの基本に関してはこの本でなくても勉強できると思うので(自分の場合はオライリーの『RESTful Webサービス』 でした)、その知識がある人は改めて読まなくてもいいように思います。

ただ、その上で設計について「こういう時はどうすればいいんだろう」みたいなよくある疑問について、選択肢を挙げた上で「私はこのやり方をおすすめします」的な 著者の意見が書かれている ので、そこに関しては1つの参考になると思います。

あとは APIのドキュメントをどう書くか 、については自分はあまり本でこの辺りを勉強していないので、いざ書こうとなったら役立つだろうと思います。Open API Specification で書く場合の具体例がたくさん出てきます。

本の構成

3部構成になっていて、第1部はAPIの基礎、第2部は設計で、この2つのパートは基本的に「理想」が書かれているように思います。

第3部からはもう少し現実的な話が出てきます。ページングやキャッシュ、バージョニングあたりの話が出てきます。

必要な章だけ独立して読めるか、でいうと、紹介されている例が基本的に続きものなので、順番に読んだ方が分かりやすいと思います。

紹介されているWebサイト

印象に残ったこと

使う側にとっての設計を常に重視する

他の開発者にとって分かりやすい設計をするべき という考えで一貫した意見を書かれていると思います。

本の中で架空の家電のUI(インターフェース)を何度も例えとして出して、「家電のUIが分かりづらかったら使いづらいですよね?表記や配置、表示単位がばらばらだったら困りますよね?APIも同じですよ」というような話をしていて、分かりやすい例えだなと思いました。

提供する側の都合で設計しない

使う側の立場で設計するということは、提供する側、つまり 開発する側の都合を設計に反映させてはいけない ということです。

データベースにこう格納されているからそれをそのまま返す、みたいなことはやめましょうという話です。この辺は個人的にはかなりやりがちではと思います。

ついでにいうと同じリソースの情報を返すAPIでも、各APIのコンテキストに必要なプロパティだけ返しましょう ということも書かれています。現実には複数のコンテキストに対応できる、プロパティがいっぱい生えたオブジェクトを返しがちだと思うので、これを守るのはちょっと手間がかかりそうです。

ヒューマン・リーダブル

APIがやりとりする内容は機械が読み書きするために存在していますが、それでも開発するのは人間なので 内容を人間が読める方がベター、というスタンスです。

例えば日付を返す時にUNIXタイムスタンプとして返すというやり方がありますが、それでは人間には分からないので "2021-03-22" のように人間が読めるようにしましょうという内容です(あるいはヒューマン・リーダブルなプロパティを別に用意する)。

他にenumをnumberで返しているところも、理解できる文字列で返した方が開発者にとってはいいという話です。

ゴールキャンバス

設計に使うツールとして「ゴールキャンバス」という表が2章以降よく出てきます。

1つ1つのAPIを、ユーザーが何かを達成するための「ゴール(目標)」ととらえて、それぞれのゴールを洗い出しながら設計するやり方を説明しています。表の列は以下の6つです:

  • 誰が
  • 何を
  • どのように
  • 入力(ソース)
  • 出力(用途)
  • ゴール

この表を埋めていくことで全体を把握して、足りないところや統一性のないところ、そして無駄なものに気付ける、というのがメリットだと思います。

HTTPプロトコルや一般的な規格を利用する

音楽プレーヤーの「再生」「一時停止」のシンボル(マーク)はISO 7000で定義されているそうで、このアイコンを知っているユーザはこれが使われているどの製品でもその使い方を推測できる、という話が6章に出てきます。

これと同じで、APIの開発者は 既に誰かが定義したものを使うことでAPIの使い方を推測しやすくできます 。また車輪の再発明を防ぐことにもなります。

以下は紹介されている規格(他)です:

  • 基本は HTTP 1.1 RFC7231
  • ステータスコード(以下はこういう使い方もできる、という例)
    • 409 Conflict … 直近で似たリクエストがあった
    • 400 … クライアント側のエラーかつカテゴリをまたぐエラーの場合 (401と403とか)
    • 404 … 403等でエラーを返すこと自体が情報漏洩になる場合
    • 207 Multi Status … 一括処理するAPIのレスポンスとして使う(項目ごとに成功やエラーの情報を含むリストを返す想定)
    • 202 Accepted … 同期的に処理できないがいずれ実行されることを表す場合(処理に時間がかかる、人の手が必要など)
    • 410 Gone … リソースが何らかの処理を表していて、それが既に処理済みか、キャンセルされた場合
  • HTTPヘッダ
    • Accept: APIのバージョン指定に使うこともできる。 (例: application/vnd.bank.2+jsonvndはvendor)
    • Accept: 同じリソースで詳しい情報を含めたものを返してほしい時に使える。 (例: application/vnd.bankingapi.extended+json)
    • Sunset (RFC8594): いつそのリソースが使えなくなるかを表すのに使える
  • 日時や期間は ISO 8601
  • 通貨は ISO 4217
  • 言語を選択させる場合は RFC5646
  • エラーのレスポンスで、ユーザが送ったJSONのどの部分に問題があるかを示したいときに JSONPath が使えるかもしれない
  • ハイパーメディアAPIの参考として (別のAPIを発見できるようにする仕組み)
    • HAL
    • Collection+JSON
    • JSON API
    • JSON-LD
    • Hydra
    • Siren
  • WebフックAPIの参考として
    • WebSub勧告
  • エラーモデルの参考として
    • Graph QLの標準のエラーモデル

拡張しやすい設計

基本的にAPIのバージョニングをしたとしても、後から設計を変更するのは大変なのでどんな構造だと拡張しやすいか、ということを知っておくといいという話です。

例えばリストをそのままJSONで返すのではなく、items のようなプロパティにリストを入れたオブジェクトを返すようにします。後からページネーションのようなメタデータを追加できるためです。

基本的にはオブジェクト > 文字列 > bool や数値の順で拡張しやすくなります。

例えば { "active": false } より { "status": "INACTIVE" } の方が後から変更しやすい(この場合はEnumの要素が増やせる)、ということかと思います。

「更新日」「作成日」のように日付を表す情報を「イベント」として表してリストの形でもっておけば、何らかの日付を追加する時に専用のプロパティを追加をする必要がない、みたいな話も載っています。

まあこれに関しては実際にうまくいくかはものによると思います。

設計の例外あるある

  • アクションリソース … リソースのアクションをHTTPメソッドで表せない場合(よく動詞でPOSTにするパターン)。これに関して正解はない
  • リソースのコレクションのうち一部を返すゴールを用意したい時
    • /transfers/delayed/{transferId} ではなく /delayed-transfers/{transferId} にすればいい(リソースIDの前は複数形、という統一感が失われない)
  • 自分自身のIDを指定する時は me というパラメータを用意することもできる (マジックリソースIDと読んでいる)
  • 一括更新/削除/置換はリソースコレクションのAPIでPATCH/DELETE/PUTを追加すればいい
  • 一括追加については単体作成用のAPI (POST /some-resources)で複数のパラメータをリストで渡せるようにするか、POST /some-resources/batch のような専用のAPIを生やす必要がある

他おすすめされていること

  • 200 などの成功のレスポンスではさらに 他のゴールを使うのに役立つ情報を返す
  • ユーザが入力パラメータとして使うとエラーになるデータを最初から返さない(使えるデータだけを返すゴールを用意する)
  • レスポンスやAPIドキュメントでプロパティやエラーを羅列する時は 使う側にとって重要なものから並べる (これは賛否両論ありそうというか実装大変そう…)
  • レスポンスのオブジェクトのネストはせいぜい3つまで
  • シーケンシャルなデータベースのキーを返すのは企業の顧客数のようなセンシティブな情報の手がかりを与えてしまうため推奨されない (分かるけどやりがち)
  • レスポンスで何かリストを返す場合は常に返せる要素数に制限を設けること(最大100個まで、とか)
  • APIのバージョニングはドメインかパスで切り替えるのが無難。一部だけ切り替えるとややこしくなりがち

最後に

ここまでで書いていないことで他にも参考にできそうなことは色々ありました。セキュリティに関して、スコープの切り方とかも説明されているのですが、実践したことがないので載せていません。

最後の13章に、設計したAPIを検証するためのチェックリストが載っていて、この辺を本当にちゃんとチェックできていれば実際使いやすいAPIになる気がします。

あとは単純にAPIのリファレンス以外にも、ユースケースを説明するドキュメントや設計ガイドラインも用意しましょうという話がありました。

この本全体を通して言えるんですが、これってWeb APIに限らず通常の開発でもやった方がいいことばかりなんだよな…と思います。APIに統一感をもたせる、推測しやすくする、必要なものだけ渡す、などもそうです。

最近出ている勉強会で「誰にでも分かる設計というのはフィクション。いい設計を目指した上で情報共有(教育など)も必要」的なことを言われている方がいましたが、まさにその通りだなと思いました。

どんなに個々のパッケージやクラスがいい設計と言えるとしても、全体像が見えるかと言われれば他に説明は必要で、APIがAPIリファレンスを見ただけではすぐには使いこなせないのと同じことかな、と思います。

まあ何が言いたいかっていうと「いい設計にしようと頑張るのも大事だけど、ドキュメントを書くなど説明する努力も大事だな…」ということでした。


余談

そういえば『ハンズオンNode.js』で「Server-Sent Event」というもの(サーバーからのみ非同期でクライアントにデータを送れる)が出てきて、そんなんあったんだ…と思いましたが、ちょっと前に聴いた mozaic.fm のどこかの回で「まだあったんですね」「使ってる人いるんですか」みたいな話になっていた気がします。結局これ活用してる人いるんですかね :thinking:

更新日時: