RESTのベストプラクティス

現在ではREST APIはとても一般的な話題です。ほとんどすべてのWebアプリケーションの一部分となっています。シンプルで一貫性があり実際的なインターフェースは必須です。これは皆さんのAPIを他の人が使うことをとても容易にします。皆さんにとってはRESTの実践が日常的に感じられるかもしれませんが、RESTをあまり尊重しない人々もよく見かけます。これがRESTについて投稿するきっかけでした。

この記事にはRESTfulなAPIを設計する時に考慮すべきベストプラクティスがあります。

注意: ここでのベストプラクティスは、私が過去の経験に基づいて良いと考える事例です。もし違う考えをお持ちであれば、お気軽にメールをくだされば意見交換できると思います。

APIのバージョンを示す

APIのバージョンは必須であるべきです。これがあると時間が経ってAPIが変わっても影響を受けません。その方法の1つはURLでAPIのバージョンを渡すことです(/api/v1/...)。

もう1つのうまいトリックは希望するバージョンを渡すAccept HTTPヘッダの利用です。Githubはそうしています

バージョンを利用すれば古いクライアントとの互換性を損なわずにAPI構造を変更できるようになります。

動詞ではなく名詞を使う

私がよく見かけるのはリソース名の中に名詞の代わりに動詞を使っている人々です。以下は悪い例です。

  • /getProducts
  • /listOrders
  • /retreiveClientByOrder?orderId=1

クリーンで一貫性のある構造のために、常に名詞を使うべきです。さらに、HTTPメソッドをうまく使えばリソース名からアクションを取り除けるようになります。以下はもっとすっきりした例です。

  • GET /products : すべての製品のリストを返す
  • POST /products : コレクションに製品を加える
  • GET /products/4 : 4番の製品を取り出す
  • PATCH/PUT /products/4 : 4番の製品を更新する

複数形を使う

私の考えでは、単一のリソースを命名する時に、単数形と複数形を混ぜることはあまり良い考えではありません。すぐに混乱して一貫性がなくなる可能性があります。

表示・削除・更新のアクションであっても、/artistの代わりに/artistsを使いましょう。

GETとHEADの呼び出しは常に安全であるべき

RFC2616HEADGETメソッドが常に安全に呼び出されるべきだと明記しています(言い換えれば、この記述は変更されるべきではありません)。

これは悪い例です。GET /deleteProduct?id=1

もし検索エンジンがそのページをインデックスしたらどうなるでしょうか。

入れ子になったリソースを使う

もしサブコレクション(他のもののコレクション)を取得したいなら、クリーンな設計のためにも入れ子になったルーチンを使いましょう。例えば、あるアーティストの全アルバムのリストを手に入れたいなら、以下のようにします。

  • GET /artists/8/albums

ページング

HTTPを使って非常に大きな結果セットを返すのもあまり良い考えではありません。大規模なJSONのシリアライズはすぐにコストがかかるようになるかもしれないので、結局はパフォーマンスの問題にぶつかることになります。

その回避策は結果のページングでしょう。FacebookやTwitter、Githubなどではそれが行われています。完了までにあまり時間がかからない呼び出しをたくさん行う方が、実行するのが非常に遅い大きな呼び出し1つよりはるかに効率的です。

また、ページネーションを使用している場合、次と前のページのリンクを示すのにいい方法はLink HTTPヘッダを通して行うことです。Githubではそれも行われています

適切なHTTPステータスコードを使う

コンテンツを返す際には(リクエストが成功してもしなくても)常に適切なHTTPステータスコードを使用してください。皆さんが自分のアプリケーションで使いたいと思う可能性のある一般的なコードを手短にまとめました。

成功コード

  • 201 Createdは、コンテンツを作成する時に使用されます(挿入)。
  • 202 Acceptedは、バックグラウンド処理(非同期タスク)のためにリクエストが処理待ちになっている時に使用されます。
  • 204 No Contentは、リクエストは正常に処理されたのにコンテンツが返されなかった時に使用されます(いい例が何かを削除する場合です)。

クライアントエラーコード

  • 400 Bad Requestは、リクエストのペイロードの処理中にエラーがあった時に使用されます(例えば、不正な形式のJSON)。
  • 401 Unauthorizedは、リクエストが認証されなかった時に使用されます(アクセストークンやユーザ名、またはパスワードが間違っている場合)。
  • 403 Forbiddenは、リクエストは正常に認証されたのに(401参照)、アクションが禁止された時に使用されます。
  • 406 Not Acceptableは、要求された形式が利用できない時に使用されます(例えば、JSONのみのサーバからXMLリソースを要求する場合)。
  • 410 Goneは、要求されたリソースが恒久的に削除されて二度と利用できない時に使用されます。
  • 422 Unprocessable Entityは、オブジェクトの作成中に検証エラーがあった時に使用できます。

ステータスコードのもっと詳しいリストはRFC2616にあります。

常に一貫性のあるエラーペイロードを返す

例外が発生した時は、エラーが記述された一貫性のあるペイロードを常に返すべきです。そうすることで、他の人がエラーメッセージをもっと解析しやすくなります(どんなエラーでも構造は常に同じになります)。

私がWebアプリケーションでよく使っている1つをご紹介します。明確かつ単純で自己記述的なものです。

HTTP/1.1 401 Unauthorized
{
    "status": "Unauthorized",
    "message": "No access token provided.",
    "request_id": "594600f4-7eec-47ca-8012-02e7b89859ce"
}