22 Jan 2013

Web API 設計のベストプラクティス集 ”Web API Design - Crafting Interfaces that Developers Love”

Apigee

Web API Design という本を読みました. Web API 設計のベストプラクティスがまとめられている, apigee という API のソリューションを提供している会社がだしているフリーの ebook です. コンパクトに 30 ページほどに読みやすくまとめられています.

以下要点のメモです.

開発者視点

  • API の目的はアプリケーション開発者 (API 利用者) が可能な限り成功すること. 開発者の視点で設計すること. 本書ではそのような API を実現する tips を紹介する.

Pragmatic REST

  • REST 原理主義はよくない. RESTafarian たるべからず
  • アプリ開発者の利便性が一番大事. 実利的な REST 設計が求められる
  • それを本書では Pragramatic REST とよぶ

URL

  • 動詞ではなく名詞
    • リソースを名詞であらわす. Collection: `/dogs/`, Specific element: `/dogs/1234`
    • HTTP メソッドをリソースの操作とする. POST は create, GET は read, PUT は update, DELETE は delete
  • 単数形よりも複数形をつかう
  • URL の階層を浅く保つ
    • リソースの関係を表したい場合. 例えばオーナー 5678 が飼っている犬をあらわす URL は `/owners/5678/dogs`
    • 目安として `/resource/identifier/resource` 以上に URL を深くすべきでない
    • URL は浅く保ち, 複雑さはクエリパラメーターに押しこむ

エラーハンドリング

  • HTTP ステータスコードをつかう
    • たかだか 10 程度のステータスコードで十分
      • Success: 200, 201
      • クライアントエラー: 400, 401, 403, 404
      • サーバエラー: 500
      • 304
  • レスポンスボディはエラーコード, エラーメッセージ, エラー詳細へのリンクを返す

例:

{
    "developerMessage": "開発者向けメッセージ",
    "userMessage": "ユーザ向けメッセージ. 必要に応じて",
    "errorCode": 1234,
    "moreInfo": "http://dev.example.com/errors/1234"
}

バージョニング

  • 必ずバージョンをつけること
  • v と整数のバージョン番号を URL のトップレベルにつける. e.g. `/v1/dogs`
    • バージョン番号にドット (マイナーバージョン) は不要. API はインタフェースであり実装ではない. マイナーバージョンは粒度が細かい.
  • バージョンは URL にいれるべきか, HTTP ヘッダにいれるべきか
    • ブラウザからの開発しやすさを考えて URL にバージョンをいれるべき
  • パラメータを URL とヘッダどちらに入るかは次のルールにしたがう
    • API のレスポンスをハンドルするロジックが変わる場合, URL にいれる
    • そうでない場合 (例えば OAuth の情報) ヘッダに入れる

Partial response と Pagination

いずれも一部の必要なデータだけをクライアント側に返す戦略. Partial response は利用者が指定したフィールドだけを返す. Pagination は返す件数を制御する方法.

  • Partial response
    • `fields` パラメータにカンマ区切りで指定
    • e.g. `/dogs?fields=name,color,location`
  • Pagination
    • `limit` と `offset` パラメータで指定する. offse 番目から limit 件取得
    • e.g. `/dogs?limit=25&offset=50`
    • 全レコード件数を metadata としてレスポンスに含めてあげる
    • 省略時のデフォルト件数はデータサイズやアプリケーションによって決める

リソース操作ではない API のデザイン

  • 計算・翻訳・変換など, ドメインによってはリソース操作でない API も存在しうる
  • その場合名詞でなく動詞をつかう
    • e.g. `/convert?from=EUR&to=CNY&amount=100`
  • ドキュメントには特殊なケースだということを明記しておくこと
    • 動詞の API はデータベースの値を返すのではなく, ロジックで計算した結果を返しているということをクリアにしておく

複数フォーマットのサポート

  • フォーマットを拡張子のように指定する
    • e.g. `/dogs.json`, `/dogs/1234.json`
  • デフォルトのフォーマットは json がベター

属性名

  • JavaScript の規約にあわせる
    • 先頭小文字のキャメルケース

例:

{"createdAt": 1320296464}

検索のための tips

  • 検索は複雑なクエリが想定されるので, Google にならって動詞の URL にするのがよい
  • `/search?q=fluffy+fur`
    • `q` に検索クエリを指定
  • 検索のスコープを絞る場合は search の前にスコープをつける
    • `/owners/5678/dogs?q=fluffy+fur`
  • 結果のフォーマットは拡張子ふうに指定
    • `/search.xml?q=fluffy+fur`

API リクエスト先をひとつの subdomain にまとめる

  • Facebook や Twitter は種類によってサブドメインがわかれているが, ひとつのまとまっている方がよい
  • 開発者ポータルも `developers.example.com` に作るとよい

例外的な動作への tips

クライアントが HTTP エラーコードをインターセプトする場合
  • たとえば Flash は HTTP のエラーレスポンスを受け取ると, エンドユーザのアプリにエラーコードを表示する
  • これを避けるために `suppress_response_codes=true` というクエリパラメータを提供する
    • これが指定されていると HTTP レスポンスコードが常に 200 になるようにする
  • レスポンスボディは通常通りのエラーコードやメッセージを返す. クライアントアプリはこれを見てハンドリングする
限られた HTTP メソッドしかサポートしないクライアントへの対応
  • 例えば PUT, DELETE をサポートしないクライアントへの対応
  • `method` クエリパラメータで指定するようにする
    • `/dogs?method=post` (create), `/dogs` (read), `/dogs/1234?method=put&location=park` (update), `/dogs/1234?method=delete` (delete)

認証

  • OAuth 2.0 を使う
  • OAuth に似た独自方式にしないこと. 既存の OAuth ライブラリが使えないと開発者は不便を感じる

Chatty APIs

  • Chatty API (おしゃべりな API, つまり情報が少なく何度も呼び出さないといけないもの) をいかに避けるか
  • まず RESTful に設計し, そのごショートカットを追加する
    • 複数の API 呼び出しを組み合わせないと実現できない使い方を, 一度の呼び出しで実現できる API を追加してあげる
    • 先にショートカットを作ってはいけない. まずは RESTful なデザインで, 必要に応じてショートカットを追加する
  • partial response にドット演算子を導入して, 別のリソースのフィールドを参照できるようにする
    • `/owners/5678?fields=name,dogs.name`

SDK

  • API がよく設計されドキュメントも整っていれば SDK は不要
  • 一方で API をつかうためにドメインの知識が必要な場合, SDK を提供するという手がある
  • API を改修するのではなく SDK を提供することで API をクリーンに保てるなどいくつかのメリットがある

API Facade Pattern

  • システムのフロント (API) とバックエンド (システム本体や DB など) のつなぎかた. Facade Pattern をつかう
  • フロントとバックエンドの間に抽象的なレイヤーを一枚はさむ
  • 設計のしかた
    1. まず理想的な API インタフェースを設計する
    2. stub を用意して上記のインタフェースを実装する
    3. facade と実際のシステムをつなぐ

参考