REST API
Overview
Webアプリケーションの設計指針の1つREST APIについてまとめるセクション。
一般的には HTTP でアクセスでき、データを XML や JSON を返すAPI。
有名REST API
概要
RESTはRepresentational State Transferという用語の略で、2000年にロイ・フィールディング(Roy Fielding)の博士論文で初めて紹介されました。ロイ・フィールディングは、HTTPの主な著者の一人で、Web(HTTP)の設計の優秀さに比べて適切に使用されていない様子を残念に思い、Webの利点を最大限に活用できるアーキテクチャとしてRESTを発表したそうです。
構成
REST APIは次のように構成されている。
- 資源(RESOURCE)URI
- 行為(Verb)HTTPメソッド
- 表現(Representations)
設計ガイド
REST API設計時、もっとも重要な項目は、次の2つに要約できる。
- URIは情報の資源を表現しなければならない。
- 資源の行為は、HTTPメソッド(GET、POST、PUT、DELETE)で表現する。
REST APIでよく使用される単語について
コレクションが、そのURLの対する全体 /users
リソースがそのURLの詳細 users/:id
コレクション
RESTfulなルーティングにおいてリソースのコレクションに対してルーティングを定義するために使われます。
コレクションに対するルーティングは、モデル全体に適用されるアクションに対して定義できる。
例 :indexアクションはコレクションに適用されるアクションであり、:showアクションはリソースに適用されるアクションです。:collectionオプションを使用すると、コントローラーで定義されたアクションに対して、URLに:idを含めないでアクセスすることができます。
リソース特定方法
URLのパスパラメーターとリクエストボディを同時に送信することは現実的。
実際、RESTful APIの設計においては、パスパラメーターを使ってリソースを指定し、リクエストボディにデータを含めて操作することが一般的。
たとえば、フォローするAPIであれば、/user/:userId/follow というURLを使って、指定したユーザーをフォローできる。リクエストボディには何も含めない場合もあります。
パラメーター種類
- クエリパラメーター
- パスパラメーター
パスパラメーター
/user/:userId という形式のURLは、パスパラメーター。
クエリパラメーター
クエリパラメーターは ? を使ってURLに付与するもの。
チェックリスト
- URLが短く入力しやすくなっているか
- URIが人間が読んで理解できるようになっているか
- URIが小文字のみで構成されているか
- URIが改造しやすくなっているか
- URIにサーバ側のアーキテクチャーが反映されていないか
- URIのルールが統一されているか
- 適切なHTTPメソッドは利用しているか
- URIで利用する単語は多くのAPIで同じ意味に利用されているものを選んでいるか
- URIで使われている名詞は複数形になっているか
- URI中にスペースやエンコードを必要とする文字が入っていないか
- URI中の単語はハイフンでつないでいるか
- ページネーションは適切に設計されているか
- ログインにはOAuth2.0を利用しているか
- レスポンスデータ形式はJSONがデフォルトになっているか
- データ形式の指定にはクエリパラメーターを使う方法をサポートしているか
- 不要なJSONPに対応していないか
- レスポンスのデータ内容はクライアントから指定できるようになっているか
- レスポンスデータに不要なエンベロープが入っていないか
- レスポンスデータの構造は可能な限りフラットになっているか
- レスポンスデータが配列ではなくオブジェクトになっているか
- レスポンスのデータ名として多くのAPIで同じ意味に利用されている一般的な単語を選んでいるか
- レスポンスのデータ名はなるべく少ない単語数で表現しているか
- レスポンスのデータ名として複数の単語を連結する場合、その連結方法はAPI全体で統一されているか
- レスポンスのデータ名として変な省略系を使用していないか
- レスポンスのデータ名の単数形・複数形はデータの内容と合っているか
- エラー時のレスポンスはクライアントが原因を切り分けられるような情報を含んでいるか
- エラーの際にHTMLが返っていないか
- 適切なステータスコードが変えるようになっているか
- メンテナンス時には503を返すようになっているか
- 適切なメディアタイプを返しているか
- 必要な場合はCORSに対応しているか
- クライアントが適切にキャッシュを行えるようにCache-Controller,Etag,Last-Modified,Varyなどのレスポンスヘッダを返しているか
- キャッシュをさせたくないデータにはCache-Controller: no-cacheが付けられているか
- APIはバージョンで管理されているか
- APIのバージョンはセマンティックバージョニングに沿ったものになっているか
- メジャーバージョン番号がURIに入っており、一目でわかるようなっているか
- APIの提供を終了する際のことを考慮に入れているか
- APIの査定提供期間をドキュメントに示しているか
- HTTPSでAPIを提供しているか
- JSONのエスケープをきちんと行なっているか
- JSONはX-Requested-Withヘッダを認識するなどSCRIPT要素では読み込めないようなっているか
- ブラウザからアクセスさせるAPIではXSRFトークンを利用しているか
- APIが受け取るパラメータはきちんと不正値(マイナスの値)などをチェックしているか
- リクエストが再送信されてもデータを再度更新してしまわないようになっているか
- レスポンスにセキュリティ対策用の各種ヘッダをきちんとつけているか
- レートリミットによる制限をしているか
- レートリミットの制限回数は想定されるユースケースに対して少なすぎないか
CRUDとREST対応表
REST APIは「データ操作 (CRUD)」を効率よく行うために作成される。
しかし、REST APIの役割はCRUD以外にも多くの処理がある。
たとえば、「確認画面の表示」や「処理完了メッセージの取得」などもREST APIの役割に含まれるため、「CRUDの概念を超えた用途」がRESTでは一般的。
RESTは「APIの設計指針」であり、CRUD操作に対応する設計方法の1つとしてRESTful APIを用いることがあります。 RESTful APIはCRUDの機能をカバーしますが、CRUDに限らず、ステータス情報の取得や通知なども設計に含められます。
CRUDはデータベース操作を指す用語で基本的な4つの処理のこと。
- C: Create(作成)→ 新しいデータを作成
- R: Read(読み取り)→ データを取得
- U: Update(更新)→ 既存データを更新
- D: Delete(削除)→ データを削除
CRUDは主に「データ操作の基本的な動作」を指し、データベース操作やアプリケーション設計に使われる。
CRUDとRESTの対応関係
| CRUD操作 | HTTPメソッド | リソースの例 | 説明 |
|---|---|---|---|
| Create | POST | /users | 新しいユーザーを作成する |
| Read(List) | GET | /users | ユーザー一覧を取得する |
| Read (Detail) | GET | /users/:id | 特定のユーザー情報を取得する |
| Update | PUT / PATCH | /users/:id | 特定のユーザー情報を更新する |
| Delete | DELETE | /users/:id | 特定のユーザー情報を削除する |
REST設計を超えて考えるAPI設計
Resource-Oriented(リソース指向)とAction-Oriented(アクション指向)のバランスについて
APIは基本リソース指向、ただし現実に合わせてアクション指向も許すのが良い。
※完全RESTではビジネス要件をカバーしきれないから
Resource-Orientedとは
あくまでリソース中心に設計すること。
- POST /orders → Orderを作成
- PATCH /orders/:id → Orderを更新
メリット: きれいなREST、理解がしやすい。
デメリット: 現実の業務要件をシンプルに表現しきれないことがある。
Action-Orientedとは
意味を持った行動中心で設計する。
- POST /orders/:id/retry
- POST /subscriptions/:id/suspend
- POST /payments/:id/retry
メリット: 現実のユースケースにダイレクトにマッチする。
デメリット: REST原則からは少し外れる(が、そこまで問題にならない時代になった)
RESTに絡めたAction-Oriented API設計ベストプラクティス
現場で「REST API」と言いつつAction-Orientedを絶対に使う場面は出てくる。 これを理解したうえで
- RESTの美しさ
- Action-Orientedの現実的な強さ
を両立できる人は、設計力が飛躍的に上がる。
1. 基本原則:まずはリソース志向(Resource-Oriented)を優先する
- 最初に考えるべきは「リソース(データ)に対するCRUD」で表現できないか。
- CRUD操作で表現できるなら、あえてアクション専用エンドポイントを作らない。
例:
POST /orders → 注文作成
PATCH /orders/:id → 注文のステータス更新(通常の状態遷移)
2. 業務ロジックが複雑なら、潔くアクション指向を選択する
リソース単体の属性更新では表現できない意味的に重要なアクションなら、専用のアクションエンドポイントを作るべき。
判断基準:
- 単なるフィールド更新ではない(=ビジネスロジックが伴う)
- ユースケースとして「特別な意味」を持つ
- クライアントが意図的にそのアクションを叩きたい
例:
POST /orders/:id/retry
POST /orders/:id/suspend
POST /subscriptions/:id/cancel
3. アクション指向エンドポイントは、HTTPメソッドを適切に使う
| メソッド | 使い方 | 例 |
|---|---|---|
| POST | 副作用を伴う明示的なアクション | POST /orders/:id/retry |
| PUT/PATCH | リソース全体または一部の属性更新 | PATCH /orders/:id |
| DELETE | リソース削除 | DELETE /orders/:id |
アクションは基本 POST。
PUTやPATCHは「リソース状態変更」だけに留めるべき。
4. エンドポイント設計に「アクションらしさ」を明示する
RESTとAction-Orientedを共存させるには、アクションエンドポイントは明確に「動詞」を含めるのがベスト。
「リソースのCRUD」 → 名詞的に設計 「アクション」 → 動詞的に設計
- 良い例
- POST /orders/:id/retry
- POST /orders/:id/suspend
- POST /users/:id/verify_email
- 悪い例
- PATCH /orders/:id でリトライを意味する(意図が見えない)
- PATCH /subscriptions/:id でキャンセルする(読んでもわからない)
5. トランザクションや副作用をAction側でしっかり管理する
アクションエンドポイントでは、たいてい複数リソースにまたがる変更が発生する。
そのためControllerで直接ゴリゴリ書かず、Service層でトランザクション管理すること。
# 例
def retry
order = find_order(params[:id])
OrderRetryService.new(order).call
render_success(order)
end
6. APIドキュメントでもActionを明示的に区別する
APIの説明でも、「これはリソースの更新」なのか「これは特別なアクション」なのか、はっきり区別する。
- リソースAPI → CRUD対応
- アクションAPI → 意味づけとビジネスルールを詳細に書く
7. 複雑なアクションはリクエストボディをちゃんと取る
単純なアクションはパスだけでいいが、条件付きアクションや追加情報を伴う場合はリクエストボディを使う。
例:
POST /orders/:id/retry
Content-Type: application/json
{
"retry_reason": "user_request",
"force_retry": true
}