この記事について
はじめてWeb API設計を担当し、設計することを通して学んだAPI設計のベストプラクティスやHTTPやRESTについて整理したいと考えこの記事を書きました。
応用的な内容よりかなり基本的な内容中心となっております。
RESTとは
Representational State Transferの略。
RESTとはWebのアーキテクチャスタイル、または設計思想のことをさす。(つまりはシステムのアーキテクチャを決定する際の羅針盤のような存在)
RESTの特徴は以下の6つ。
- クライアント/サーバ
- ステートレスサーバ
- キャッシュ
- 統一インターフェース
- 階層化システム
- コードオンデマンド
上記の特徴にしたがっていることをRESTfulといい、RESTの原則に基づいて設計されているAPIをRESTful APIと呼ぶ。
クライアント/サーバー
クライアントとサーバーを分離し、それぞれを個別に進化させるという考え。
クライアントはユーザーインターフェースを担当し、サーバーはデータ処理や保存を担当することでクライアント側はマルチプラットフォーム化できるほか、サーバーは冗長化し可用性を高めることができる。
このことからお互いに影響を受けずに独立して更新・拡張をすることができる。
ステートレスサーバ
クライアントのアプリケーション状態をサーバー側で管理しないことを意味する。
サーバーはクライアントからのリクエストのみに依存する。
このためクライアントからの処理を終えるとサーバーはすぐに計算リソースを解放することができる。
キャッシュ
一度取得したリソースをクライアント側で保存して使い回すこと。
サーバーとクライアント間の通信を減らしてより効率的に処理ができるようにするための仕組み。
ただし、場合によっては古い情報が返ってしまうため注意する。
統一インターフェース
リソースに対する操作を統一されたインターフェースで行う。
URIでリソースを指定し、リソースへの操作を8つのHTTPメソッドで定義することによってシンプルで直感的な操作となる。
階層化システム
クライアントとサーバーの間にロードバランサーやプロキシなどを設置していくつかの階層に分離するアーキテクチャスタイル。
インターフェースが統一されているため接続先がサーバーであろうがプロキシであってもクライアントは意識する必要がなくなる。
コードオンデマンド
RESTの6つの特徴のうち唯一のオプショナルな特徴。
コードをサーバーからダウンロードし、クライアント側で実行するアーキテクチャスタイルを指す。
HTTPメソッド
クライアントはHTTPリクエストをサーバーに送信し、処理をサーバーに依頼する。
HTTPリクエストは
- リクエストライン
- リクエストヘッダ
- リクエストボディ
の構成となっていて、リクエストライン内のHTTPメソッドを使用してクライアントが行いたい処理をサーバーに伝えている。(つまりクライアントがどうしたいかをHTTPメソッドを通してサーバーに伝えている。)
RESTful APIで使用頻度が高いメソッドはGET、POST、PATCH、DELETEの4つ。
GET
URIで指定されたリソースを取得するHTTPメソッド。
HTTPメソッドの中で一番使われる。
POST
新しい情報を登録するために利用する。(情報を更新するメソッドではない。)
情報を新規登録する場合にはPOSTが適当だが、削除するときにはPUTやDELETEを使うべき
PUT
既存リソースを完全に上書きするメソッド。
URIで指定されたリソースがない場合には新しいリソースを作成することも可能だが、新しいリソースを作成するときにはPOSTを利用するべき。
POSTの場合はリソースが存在しない場合にURIの決定権はサーバーにあり自動的にURIが割り当てられるが、PUTの場合にはURIの決定権はクライアントにありクライアントとサーバーの結合が密になるためPUTはリソースの更新のみに使うべき。
PATCH
PUTと異なり全てを変更するのではなく、一部を変更するメソッド。
DELETE
URIで指定されたリソースを削除するメソッド。
ステータスコード
ステータスコードはレスポンスメッセージを意味するもの。
正しくステータスコードを使わないとクライアントを混乱させるため、仕様に定められた正しいステータスコードを使う。
ステータスコードは3桁の数字で表され、先頭の数字で5つに分類される。
| ステータスコード | 内容 |
|---|---|
| 100番台 | 処理中 |
| 200番台 | 成功 |
| 300番台 | リダイレクト |
| 400番台 | クライアントエラー |
| 500番台 | サーバーエラー |
200 OK
リクエストが成功したことを示す。
リクエストのHTTPメソッドによって成功が表すことは異なる。
GETの場合にはレスポンスボディに読み込まれたリソースが入り、PUTやPOSTの場合には処理の結果がボディに入る。
201 Created
リクエストが成功し、その結果新しいリソースが作成されたことを示す。POSTやPUTのレスポンスとして返る。
POSTの場合、新しく作成したリソースのURIがLocationヘッダで返される。
400 Bad Request
リクエストの構文やパラメータが間違っているためサーバーがリクエストを理解できないことを示す。
401 Unauthorized
クライアントがリクエストを行うためには認証されている必要があることを示す。
403 Forbidden
クライアントは認証されているが、クライアントがリクエストをする権限がないことを示す。
404 Not found
URIで指定されたリソースがサーバー上に存在しないことを示す。
503 Service Unavailable
サーバーが何かしらの原因(過負荷やサーバーダウン)でクライアントからのリクエストを受け付けることができない状況。
エンドポイント設計
短く入力しやすい
無駄な情報を省いてわかりやすいAPIにする。
【BAD】
https://api.example.com/services/api/products
【GOOD】
https://api.example.com/products
人間が読んで理解できるURI
短くするあまり極端に略しすぎない。
【BAD】
https://api.example.com/p
【GOOD】
https://api.example.com/products
大文字小文字が混在していない
基本的に小文字を使用する。
【BAD】
https://api.example.com/Products
【GOOD】
https://api.example.com/products
英単語の誤用には気をつける
意味としては同じだが微妙にニュアンスが異なる英単語には気をつける。
例えば飲食店の予約管理をするAPIを設計する際に「予約」の意味となる単語はreservationとappointmentのふたつがあるが、reservationは場所に対しての予約、appointmentは人に対しての予約という意味の違いがある。そのためこの場合であればresevationがふさわしい単語になる。
スペースやエンコードを必要する文字は使わない
URIには利用できない文字があり、そういった文字はパーセントエンコーディングされるため、とても見にくいURIとなるので使わない。
※パーセントエンコーディングとはURIに使えない文字列を半角英数字の組み合わせに置き換えること。
また、ホワイトスペースは+で変換される。パーセントエンコーディングほど読みにくくはないが美しくないので使うのは避ける。
単語を繋げる必要がある場合にはハイフンを使用する
特別決められたルールはないが
- URI中のホスト名はハイフンを許可されているがアンダースコアは使えない
- ドットは特別な意味を持つ
ということからURI全体でルールを保つためにもハイフンで単語を繋ぐのがベター。
※ただ複数の単語を繋ぎ合わせることは極力避ける。使う必要が出てきた場合にはまず設計を見直して回避できないか考える。
一意なリソースを表す場合はパスを使う
例えば商品を取得するAPIで、商品idを指定することで一意の商品が決まるためパスの中に入れるのが良い。
https://api.example.com/products/1
省略可能であればクエリパラメータを使う
sortやlimitのように特定のものに条件を付け加えて、条件がなければデフォルトのものが適用されるケースにはクエリパラメータを使う。
https://api.example.com/products?sort=true?limit=10
名詞の複数形を使用する
APIはリソースを表すもののため、URIには名詞を使用する。
またリソースの集合体のため複数形とする。
レスポンスデータ設計
エンベロープを使わない
以下の理由からエンベロープは使うべきではない。
- HTTPがエンベロープの役割を果たしている
- 1.からエンベロープを使っているレスポンスは冗長
すべてのAPIが同じAPIの共通構造を返すために実際のレスポンデータをラップしていることをエンベロープという。以下の例で言えばheaderやresultがエンベロープにあたる。
{
"header": {
"code": "0",
"message": "success"
},
"result": {
"vegetables": [
{
"id": 1,
"name": "にんじん",
"price": 100
},
{
"id": 2,
"name": "たまねぎ",
"price": 150
}
]
}
}
Web APIはHTTPを利用していて、HTTPヘッダーやレスポンスボディがエンベロープで使われているheaderやresultの役割を果たしている。また後述するがレスポンスヘッダでは200を返すが、レスポンスボディ内で404を返した場合にはクライアント側でエラーが起きていることを認知できない可能性があるためレスポンスヘッダで正しいステータスコードを返す。
エラーはステータスコードで返す
クライアントのライブラリではレスポンスのステータスコードを使ってエラー分岐をしている場合があり、正しいステータスコードを返さないとエラー処理が行えない可能性がある。
そのためリクエストが成功した場合には200番台、クライアント側に問題がある場合には400番台、 サーバー側に問題がある場合には500番台のステータスコードを返す。
また、クライアントからのリクエストに成功した時のみ200番台のステータスコードを返してはならない。
レスポンスボディでエラーの詳細を返す
ステータスコードは汎用的かつ一般的なものなのでエラーの詳細まではわからず不十分である。
そのためレスポンスヘッダやレスポンスボディでエラーの詳細をクライアントに返す。
データの構造はできる限りフラットにする
ネストするとJSONのデータサイズが大きくなってしまうため不要なネストは避ける。
ただし、ネストした方がわかりやすい場合にはネストも取り入れる。
fieldsパラメータなどを使う
APIを叩く回数を減らすためにたくさんのデータを返すことで解決ができるが、大量のデータをクライアントが受け取らなければならなくなりクライアントへの負担が増える。
そのためできる限りレスポンスデータを抑えるためにfieldsパラメータなどを使ってクライアントがレスポンスデータを選択できる仕組みを作る。
感想
API設計を始めるのであればWeb API The Good Partsを読むべきと先輩エンジニアから勧めてもらいましたがその通りでした。
またHTTPの仕組みを最大限活用しながらAPIを設計することが大切だとも感じたのでWebを支える技術も参考にしながら設計するのがとても良いと感じました。
参考
Webを支える技術
Web API The Good Parts
API設計スキルを次のレベルに引き上げるベストプラクティス22選
API設計まとめ
[RESTful API]パスパラメータ、クエリパラメータ、リクエストボディの違いと設計