Open29

REST API 考え方

syysyy

良いURIとは

どんな機能を持つか推測しやすい
・短い
・理解できる
・すべて小文字
・推測可能
・一貫性がある
・サーバーアーキテクチャに左右されない

syysyy

短いURI

同じことを表しているなら短くてシンプルな方が良い

http://api.hoge.com/service/api/search
http://api.hoge.com/search
syysyy

理解できる

パット見で用途がわかるようにする
・省略しない
・適切な単語を使用する。単語を一般的なものに合わせる
・過去形, 複数形を間違えない

http://api.hoge.com/u/
http://api.hoge.com/user/

# findよりsearchの方が一般的
http://api.hoge.com/find
http://api.hoge.com/search

# registという単語はない
http://api.hoge.com/regist
http://api.hoge.com/register
syysyy

すべて小文字

大文字小文字が入り組んでいると見にくい
(google cloud apiなどでキャメルケースを使用しているケースもある)

syysyy

推測可能

↓12345がitemIdで、他のitemIdを入れれば違うItemの情報が見れることが推測できる。

http://api.hoge.com/items/12345
syysyy

サーバーアーキテクチャに左右されない

・サーバーアーキテクチャ変更時にpathが影響されると辛い
・API利用者にサーバー側の情報は不要

http://api.hoge.com/items/get.php
syysyy

一貫性がある

同じフォーマットの方が分かりやすい

http://api.hoge.com/items/100
http://api.hoge.com/customer/get?customer=123
syysyy

method

method 用途
GET 取得
POST 登録
PUT 更新
PATCH 一部更新
DELETE 削除
HEAD メタ情報取得
syysyy

relationの例
user -> friend

Head Head Head
http://api.hoge.com/users/:userId/friends GET フレンド一覧取得
http://api.hoge.com/users/:userId/friends POST フレンド追加
http://api.hoge.com/users/:userId/friends/:friendId GET フレンド取得
http://api.hoge.com/users/:userId/friends/:friendId DELETE フレンド削除

:fiendIdはフレンドのuserIdか、フレンド関係を表すテーブルのidのどちらが良いか

フレンドのuserIdの方が良い。
=> アーキテクチャに依存しない方が良い
=> フレンドidの方が推測可能

syysyy

検索

検索パラメータ
・取得数(par_page, limit)
・取得位置(page, offset)

組み合わせ
・par_pageとpage
・limitとoffset

pageは1始まり, offsetは0始まり
pageはページ数を、offsetはアイテム数を指定する

相対位置による検索

・limitとoffsetによる検索はDBの負荷が大きくなる
・途中でinsertされると取得位置がずれる

絶対位置による検索

どこから検索するかパラメータで指定する(最後に取得したidや時刻など)

検索クエリ

q="hoge" : 部分一致しそう
name="hoge" : 完全一致しそう

syysyy

検索エンドポイントのURIにsearchは入れるべきか?

・入れるとリソースを指すREST APIとは思想が離れる
・エンドポイントを検索用として強調しないなら入れても良さそう

syysyy

クエリパラメータ or pathどちらにすべきか

ポイント
・URIはリソースを表現するためのもの
・省略可能かどうか

リソース特定になるならpath
省略可能ならクエリパラメータ(省略してデフォルト値使用できるので)

syysyy

自己参照

自分自身を指すmeやselfキーワードを使うことでより便利なエンドポイントにできる。
自分のidを知らなくてもエンドポイントが決まる。

http://api.hoge.com/users/me
syysyy

LSUDsとSSKDs

どちらにも言えることだけど、汎用的で分かりやすいものが良い

LSUDs
・Large Set of Unknown Developers
・大多数の外部developer向け

SSKDs
・Small Set of Known Developers
・コントロール可能な開発者向け(社内サービス開発エンジニアとか)

社内サービス開発はSSKDsになる。
社内サービス開発では、そのサービスのUXを高めることに注力してAPIを開発すべき。(汎用化しすぎて複数のAPIを実行しないとアプリが起動しなかったらUX最悪)

syysyy

どの情報を返すかユーザーが選べるようにすると便利
不要なデータを返すとレスポンスサイズが無駄に大きく時間がかかる。

http://api.hoge.com/v1/users/111?fields=name,age

予めいくつかのfieldを組み合わせて提供する方法もある

small = name, email, age...
http://api.hoge.com/v1/users/111?fields=small
syysyy

適切なエンベロープを選ぶ

エンベロープ = データ構造

↓レスポンスにheadersを入れなくてもレスポンスヘッダーを使えば良い。不要なデータ構造を含まないようにする

{
  headers : { 略 }
  body : { 略 }
}
syysyy

データ構造はフラットに。でも階層構造を持ったほうが良い場合は使ってもok。

↓ 無理に階層構造持たせる必要はない

{
  name: "hoge"
  age: 13
  profile: {
    nickname: "ho"
  }
}
{
  name: "hoge"
  age: 13
  nickname: "ho"
}

↓階層構造があるとわかりやすく、データ量が少なくできるかも

{
  name: "hoge",
  age: 13,
  hoge : {
    id: 1
    fuga: 0    
  }
}
{
  name: "hoge",
  age: 13,
  hoge_id: 1
  hoge_fuga: 0
}
syysyy

配列を返すときのフォーマット
そのまま配列を返すか、オブジェクトの中に配列を入れるか。
jsonの仕様的には配列をそのまま入れてもok

[
 { name: "hoge", age: 1 },
 { name: "piyo", age: 1 },
]
{
  users: [
   { name: "hoge", age: 1 },
   { name: "piyo", age: 1 },
  ]
}

どちらも大差ない。好みの問題になる
・オブジェクトにするとmetaを追加で入れやすい。拡張性考えるならオブジェクトの方が良さそう。
・オブジェクトにする場合は、そのレスポンスを見て人間がなんのデータかひと目で分かる
・配列そのまま返せばreact + typescriptだとキャストしやすい?

syysyy

ページネーション

・全件数が必要?
=> db負荷かかるから注意
=> 普通にcount(*)するだけだとレコード数の増加に伴ってレスポンスが遅くなりがち
=> ↓回避する方法もある(ロールバックで欠番出る可能性あるから厳密なケースには使えないけど)
https://blog.kamipo.net/entry/20100128/1264684675
http://nippondanji.blogspot.com/2010/03/innodbcount.html

・続きがあるかどうかを返すのもあり
クライアントに続きあるかのフラグを見て判断してもらう。(UI的に厳しそうだけど)
このケースの場合、全件数を取得する必要がない。
limit 20件なら21件取得して、21件取れれば次がある、20件以下なら次はないと判断できる

{
  hoges [ {}, {}, {}... ],
  hasNext: false
}

↑こういうデータ構造にするなら配列だけ返すのではなくオブジェクトにしたほうが改修楽そう

・次ページに必要なパラメータを返す
HATEOASという考え方。
next_idとか返せば良さそう。

syysyy

エラーレスポンス

・ステータスコードを正しく使う
200系 : 正常終了
300系 : リダイレクト
400系 : クライアントに問題あり
500系 : サーバーに問題あり

・エラーレスポンス返すならステータスコードは400 or 500系にする。
レスポンスボディにエラーメッセージを入れていても正しくステータスコードを入れる。
クライアントライブラリはステータスコードを見てエラーハンドリングするため

・エラーレスポンスのフォーマット
参考
https://qiita.com/suin/items/f7ac4de914e9f3f35884#google

・エラーレスポンスがうっかりhtmlにならないようにする
jsonが期待されているならjsonを返す
特に404のときとか

syysyy

成功ステータスコード
200系

200 : ok
201 : 何か作成した
202 : accept。処理を受け付けたが完了していない
204 : not content。削除成功時

202に注意。
リクエストに応じて何かしらの非同期処理が行われる場合、リクエストは202で返す。
(ファイルの変換リクエストなど)

syysyy

作成/変更/削除時のレスポンス
何をレスポンスするか。

作成(POST) : 作成したデータ。201
更新(PUT/PATCH) : 更新後のデータ。200
削除(DELETE): 空。204ステータスコード

syysyy

クライアントエラーステータスコード
400系

400 : Bad Request。他のステータスコードでは合わない場合
401 : 認証エラー Unauthorized
403 : 認可エラー Forbidden
404 : Not found。ないよ
429 : too many requests。スロットル制限

syysyy

サーバーエラーステータスコード
500系

500 : Internal Server Error
503 : メンテナンス中, インスタンスに問題があるなどの場合にGatewayが代わりにエラーを出す