「Web API The Good Part」を読んだ

タイトルのとおり、「Web API The Good Parts」を読んでのまとめです。

第1章 Web APIとは何か

  • この本でいうWeb APIは「HTTPプロトコルを利用してネットワーク越しに呼び出すAPI

Web APIを美しく設計する重要性Web APIを美しく設計した方が良い理由は4つある。

  • 使いやすい
  • 変更しやすい
  • 頑強である
  • 恥ずかしくない

使いやすい

APIを利用するのは自分ではないケースが多い。設計によって使いやすさが大きく異なってきて、開発期間や開発者のストレスに関わってくる。

変更しやすい

Webサービスやシステムはどんどん進化していくため、公開当時と全く同じ状態で2、3年運用が続けられることはない。サービスが変化すれば、そのインターフェースであるAPIも変化を余儀なくされる。自分たちと関係ない第三者が利用している場合、APIの仕様を大きく変えたことで動かなくなってしまうという事態は避けたい。特にモバイルアプリケーションの場合、アップデートのタイミングはユーザ次第なので、APIの仕様変更により古いアプリケーションが動かなくなってしまう。APIを美しく設計することの意味の中には、APIの変化をいかに利用者に影響なく行うか、ということも含まれる。

頑強である

APIもWebサイト同様にセキュリティ問題を考慮する必要がある。これに加えてAPIならでは問題もある。こうした問題をきちんと考慮しているAPIが美しいAPIである。

恥ずかしくない

Web APIは開発者が目にするもの。コードや設計によって技術レベルを判断されてしまうことがある。美しくないAPIを実装してしまうと、採用等にも影響が出てくる可能性がある。

第2章 エンドポイントの設計とリクエストの形式

APIエンドポイントの考え方

APIで提供する機能を決めたら、エンドポイントを考えながらAPIを整理していく。Web APIにおけるエンドポイントとは、APIにアクセスするためのURIのことを指す。APIの利用者はこのエンドポイントにアクセスすることで、APIの機能を利用できる。

エンドポイントの基本的な設計良い設計とは、「覚えやすく、どんな機能を持つURIなのかがひと目でわかる」ものである。美しいURIを設計する方法について、一般的に重要な事柄は次の通りです。

  • 短く入力しやすいURI:理解しやすく、覚えやすく、入力間違いが少ない。
  • 人間が読んで理解できるURIURIを見ればそれ以外の情報がなくてもそれが何の目的としたものかがある程度わかる。極力省略形は使わないようにする。「この単語はこういう機能、情報を表す」という共通認識があることが多いので、APIでよく利用されている英単語を利用する。
  • 大文字小文字が混在していないURI:基本はすべて小文字を使う。大文字小文字の混在は、APIをわかりづらく、間違えやすくする。小文字のエンドポイント対して、大文字でアクセスしてきた場合は、404 NotFoundを返すケースが多くなっている。そもそもHTTPにおいてURIは「スキーマとホスト名を除いては大文字と小文字は区別される」とRFC 7230に書いてある。
  • 改造しやすい(Hackableな)URIURIを修正して別のURIにするのが容易であることを意味する。
  • サーバ側のアーキテクチャが反映されていないURI:Webアプリケーションでは、URIがサーバ側のアーキテクチャディレクトリ構造を反映する必要は全くない。アーキテクチャを特定できるURIにしてしまうと、攻撃されやすくなる。
  • ルールが統一されたURI:ルールが統一化されていないとクライアントを実装する場合に混乱を招きやすく、トラブルの温床になる。

HTTPメソッドとエンドポイント

HTTPメソッドは、HTTPでのアクセス時に指定するもので、GET/POSTなどが有名。URIとメソッドの関係は、操作するものと操作方法の関係である。URIAPIにおいて、"操作する対象=リソース"を表すものだとすれば、HTTPメソッドは"何をするか"を表すもの。1つのURIのエンドポイントに異なるメソッドでアクセスすることで、情報を取得するだけでなく、情報を変更したり、削除したり様々な操作を行うようにすることで、リソースとそれをどう扱うかをきちんと分離して扱うことができる。

GETメソッド

情報の取得を表すメソッド。

POSTメソッド

指定したURIに属する新しいリソースを送信する、つまり新しい情報を登録するために利用するメソッド。

PUTメソッド

更新したいリソースのURIそのものを指定し、その内容を書き換えるメソッド。また、PUTは送信するデータでもともとのリソースを完全に上書きするというもの。

DELETEメソッド

リソースの削除を行うメソッド。

PATCHメソッド

一部を変更することを明示したメソッド。PUTが送信したデータでもともとのリソースを置き換えるものであるのに対し、PATCHではその一部だけを更新したい場合に使う。

APIのエンドポイント設計

ここの例であげられているエンドポイントは、「/users」と「/users/:id」の2つ。これらはそれぞれ「ユーザの集合」と「個々のユーザ」を表すエンドポイントである。この2つの概念は、データベースで例えるならテーブル名とレコードの関係だと言える。そしてそのテーブルやレコードに対してどんな処理を行うかを表しているのがHTTPメソッド。エンドポイントを設計する中で注意すべき点

  • 複数形の名詞を利用する:名詞の複数形を使って「リソースの集合」を表している。HTTPのURIがそもそもリソースを表すものである考え方からきている。
  • 利用する単語を気をつける:利用する単語に困ったら複数のAPIを調べてより多く使われている単語を選択する。
  • スペースやエンコードを必要とする文字を使わない:そのエンドポイントがどのようなものかが一目でわからないため、エンドポイントにはパーセントエンコーディングされた文字が入らないようにすべき。
  • 単語をつなげる必要がある場合はハイフンを利用する:Webページで場ではリンクアドレスに下線が引かれることが多く、アンダースコアだとこれと重なってしまうため見づらい、アンダースコアは歴史的にタイプライターで下線を引くためのものなので目的にそぐわないといった話がある。

単語をつなげる際、スネークケース、キャメルケース、スパイナルケース(またはチェインケースやケバブケース)で迷った際はスパイナルケースにしておくのが無難。理由は、URI中のホストはハイフンを許可されているが、アンダースコアは使えず、大文字小文字の区別がなく、ドットは特別な意味を持つため、ホスト名と同じルールでURI全体を統一しようとするとハイフンでつなぐのが最も適していることになるからである。

検索とクエリパラメータの設計

絞り込みのパラメータを実装すると検索を行うことができる。そのときに利用するのがクエリパラメータ。

クエリパラメータをパスの使い分けクエリパラメータに入れるのかパスに入れるのかはURIを設計する上で必要になるが、その判断基準は以下のようにするとよい。

  • 一意なリソースを表すのに必要な情報かどうか:ユーザIDを指定することで参照したい情報が一意に決まるのでパスに入れた方がよい。リソースとは関係ないアクセストークンのようなものはクエリパラメータの方が適している。
  • 省略可能かどうか:リストや検索の際のoffsetやlimitあるいはpageなどのパラメータは省略すればデフォルトの値が利用されるケースが多くなるため、クエリパラメータの方が適している。

第4章 HTTPの仕様を最大限利用する

HTTPの仕様を利用する意義

インターネット上で利用される仕様の多くはRFC(Request for Comments)と呼ばれる仕様書で定義されている。こういった仕様をよく理解することで不本意に独自仕様を入れてしまう危険性が減る。また、標準の仕様をできる限り利用して作られたAPIは第三者にとっても、少なくとも独自仕様に比べればずっと理解しやすい。

ステータスコードを正しく使う

ステータスコードは先頭の数字1桁でおおよその意味合いを示している。

ステータスコード 意味
100番台 情報
200番台 成功
300番台 リダイレクト
400番台 クライアントサイドに起因するエラー
500番台 サーバサイドに起因するエラー

HTTPステータスコードには意味があるので、適切なコードを返した方が、クライアントがエラーを正しく認識することができる。

200番台:成功

指定したデータの取得が成功した、あるいはリクエスト処理が成功した場合には200番台のステータスコードを返す。 202の"Accepted"は、リクエストした処理が非同期で行われ、処理は受け付けたけど完了していないあ愛に利用される。 204の"No Content"は、レスポンスが空のときに返す。DELETEメソッドなどでデータの削除を行った際に、204を返す。

300番台:追加で処理が必要

300番台のステータスコードのうち、リダイレクトに関するものは301, 302, 303, 307がある。リダイレクトの場合はLocationというレスポンスヘッダにリダイレクト先の新しいURIが含まれる。 300の"Multiple Choices"は、複数の選択肢がある場合に送信されるステータスコード。 304の"Not Modified"は、前回のデータ取得から更新されていないことを表すステータスコードでレスポンスボディは空。

400番台:クライアントのリクエストに問題があった場合

400番台はクライアントのリクエストに起因するエラー、サーバ側に問題がないが、クライアントの送ってきたリクエストが理解できなかったり、理解はできるが実行が許可されていなかったりしてエラーになった場合に利用するステータスコード。 400の"Bad Request"は、その他。他の400番台のエラーで表すことができないエラーに使うためのステータスコード。 401の"Unauthorized"は、認証のエラー(あなたは誰だかわからない)、403の"Forbidden"は、認可のエラー(あなたが誰だかわかったけどこの操作はあなたには許可されていない)をそれぞれ表す。 408の"Request Timeout"は、リクエストをクライアントがサーバに送るのに時間がかかりすぎてサーバ側でタイムアウトを起こした際に発生する。 410の"Gone"は、かつて存在したけれども今はもう存在しないということを表している。例えば、かつて登録されていたメールアドレスが削除されていた際に使うがこれは、セキュリティの観点から指摘される可能性がある。

500番台:サーバに問題があった場合

500番台のエラーは、クライアント側でなくサーバ側に問題があった場合のエラー。 503の"Internal Server Error"は、意図的がどうかは問わず、サーバが一時的に利用できな状態になっていることを示すもの。

キャッシュとHTTPの仕様

ここでいうキャッシュは、サーバへのアクセスの頻度や通信量を減らすためにクライアント側で一度とった情報を保存しておき、再度必要となったときにあらかじめ取得してあった情報を利用することをいう。 キャッシュのメリットは次の通り。

  • サーバへの通信を減らすことができるため、ユーザの体感速度をあげることができる
  • ネットワーク接続が切れた状態でもある程度サービスを継続できる
  • サーバへの通信回数、転送量を減らすことでユーザの通信コストを下げることができる
  • サーバへのアクセス回数が減ることで、サーバの維持費用を抑えることができる

HTTPのキャッシュでは、RFC 7234できちんと定義されている。HTTPのキャッシュにはExpiration Model(期限切れモデル)とValidation Model(検証モデル)という2つのタイプがある。

Expiration Model(期限切れモデル)

期限切れモデルは、あらかじめレスポンスデータに保存期限を決めておき、期限が切れたら再度アクセスして取得を行うというもの。Cache-Controlレスポンスヘッダを使う方法かExpiresレスポンスヘッダを使うを使うことで、いつ期限が切れるかをサーバからのレスポンスをに含めて返すことで実現できる。 ExpiresとCache-Controlを同時に利用した場合には、より新しい仕様であるCache-Controlが優先される。

Expiresヘッダ

ExpiresはHTTP 1.0から存在するヘッダで期限切れを絶対時間でRFC 1123で定義された形式で表す。特定の日時に更新されることがあらかじめわかっているデータは、Expiresで日時を指定することができる。また、今後更新される可能性がないデータや静的データの場合には、遠い未来の日時を指定することで、一度とったキャッシュデータ をずっと保存しておくように指示を出すことができる。

Cache-Controlヘッダ

Cache-ControlはHTTP 1.1から定義されたヘッダで現在時刻からの秒数で表す。「毎日何時」など定期更新でないものの更新頻度がある程度限られているものや、更新頻度は低くないものの、あまりアクセスしてほしくない場合に利用することができる。 max-ageの計算には、Dateヘッダを利用する。これはレスポンスが生成されたサーバ側の日時を示すヘッダで、この日時からの経過時間がmax-ageの値を超えた場合にはそのキャッシュは期限が切れたと考えることができる。

Validation Model(検証モデル)

検証モデルは、今持っているキャッシュが有効かどうかをサーバに問い合わせるというもの。過去に取得したある時点でのデータに関する情報を送り、更新されていたらデータを返し、更新されていなかったら304("Not Modified")というステータスコードを返す。

キャッシュをさせたくない場合

Cache-Control: no-cache

このようにCache-Controlヘッダに指定することで「キャッシュをしてほしくない」と伝えることができる。

第5章 設計変更をしやすいWeb APIを作る。

機能の強化やバグの修正、機能の廃止など状況に応じて、変化していく。

APIをバージョンで管理する

古い形式でアクセスしてきているクライアントに対してはそれまでと変わらないデータを送り、新しい形式でのアクセスには、新しい形式のデータを返す。 バージョニングの方法には、次の3つが主に考えられる。

  • URIにバージョンを埋め込む
  • バージョンをクエリ文字列に入れる
  • メディアタイプでバージョンを指定する

最も利用されているのは、「URIにバージョンを埋め込む」である。URIを見るだけでAPIのバージョンがはっきりわかるため、受け入れられやすいようだ。

バージョン番号をどうつけるか

バージョニングのルールとして広く知られている方法にセマンティックバージョニングがある。"1.2.3"のように3つの数値をドットで繋いだもの。それぞれの数値は、メジャー、マイナー、パッチと呼ばれ、以下のようなルールが適用される。

  • パッチバージョンはソフトウェアのAPIに変更がないバグ修正などを行なったときに増える。 マイナーバージョンは後方互換性のある機能変更、あるいは特定の機能が今後廃止されることが決まった場合に増える。- メジャーバージョンは後方互換性のない変更が行われた際に増える。

APIの提供を終了する

APIが公開終了になったときに、どういったことが起こるかをあらかじめ仕様に盛り込んでおくと良い。一番簡単なのは、APIが公開終了した際にはステータスコード410(Gone)を返すというもの。410を返すだけでは不親切なので、エラーメッセージとして「このAPIは公開が終了しました。より新しいバージョンを使ってください」のようなメッセージを返すとよりよい。