Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
RESTful API の設計のキホン
Search
Cside
October 12, 2016
Programming
94
17k
RESTful API の設計のキホン
2016/10/12 社内勉強会で使ったスライドを社外向けに一部加筆訂正したもの
Cside
October 12, 2016
Tweet
Share
More Decks by Cside
See All by Cside
さくらVPS、DotCloudの次にくるRackhub
cside_
5
8.8k
iPhoneアプリ「Music Stream(仮)」紹介 ( #on_lab )
cside_
1
1.2k
Other Decks in Programming
See All in Programming
What’s New in Compose Multiplatform - A Live Tour (droidcon London 2024)
zsmb
1
470
카카오페이는 어떻게 수천만 결제를 처리할까? 우아한 결제 분산락 노하우
kakao
PRO
0
110
CSC509 Lecture 09
javiergs
PRO
0
140
Streams APIとTCPフロー制御 / Web Streams API and TCP flow control
tasshi
2
350
Ethereum_.pdf
nekomatu
0
460
ECS Service Connectのこれまでのアップデートと今後のRoadmapを見てみる
tkikuc
2
250
とにかくAWS GameDay!AWSは世界の共通言語! / Anyway, AWS GameDay! AWS is the world's lingua franca!
seike460
PRO
1
860
距離関数を極める! / SESSIONS 2024
gam0022
0
280
シールドクラスをはじめよう / Getting Started with Sealed Classes
mackey0225
4
640
【Kaigi on Rails 2024】YOUTRUST スポンサーLT
krpk1900
1
330
GitHub Actionsのキャッシュと手を挙げることの大切さとそれに必要なこと
satoshi256kbyte
5
430
Jakarta EE meets AI
ivargrimstad
0
580
Featured
See All Featured
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Automating Front-end Workflow
addyosmani
1366
200k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Optimising Largest Contentful Paint
csswizardry
33
2.9k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Ruby is Unlike a Banana
tanoku
97
11k
A Tale of Four Properties
chriscoyier
156
23k
Teambox: Starting and Learning
jrom
133
8.8k
Git: the NoSQL Database
bkeepers
PRO
427
64k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
What's in a price? How to price your products and services
michaelherold
243
12k
Transcript
RESTful API の設計のキホン 2016/10/12 Hiroki Honda @Cside_
なぜこの会を開いたか • 自分の関わった某案件で、API の新エンドポイントのインターフェイスをレビューす るときに「こういう風に直して下さい」と指示はしてきたけれど、「なぜそのように直さ なければならないのか」までちゃんと説明しきれてなかった ◦ 納得できてない人もいると思う ◦ なのでちゃんと説明したい
◦ そして、誰でも設計できるようになってほしい(重要)
おことわり • API の設計に正解はなく、これから話す指針はあくまで「私個人の考える Good Practice 」であることをご了承下さい ◦ マウンティングやめてね …
。 ◦ なるべく、他にどういう流派があるかはあわせて説明するようにします
第一章 REST と RPC
Web API のスタイル • REST スタイル • RPC スタイル が主なもの。
REST スタイルの特徴 • REST スタイルの API 設計では ROA( Resource Oriented
Architecture )という 手法が広く知られている • Web API を RESTful にする == ROA にそって設計する • なので ROA の特徴をしっかり理解するのが重要
ROA の 4 つの特徴 • アドレス可能性 ◦ リソースがURIを通して表現できること • ステートレス性
◦ APIリクエストのためのHTTPリクエストがすべて分離・独立していること(前の リクエストに影響されたりしない) • 接続性 ◦ リソースは別のリソースとの関連を表すリンクを持ちうること • 統一インタフェース ◦ HTTP メソッドを用いてリソースを参照/更新すること ◦ 後述
リソースとは • URI を持ったデータのこと
リソースの例 • 例えば、モバゲーにおける僕のプロフィールというリソースは以下の URI を持って いる ◦ /api/restful/v1/people/32592054/@self
リソースの例 • GET すると以下のようなレスポンスが返ってくる (※ 形式は簡略化しています) GET /api/restful/v1/people/32592054/@self HTTP/1.1 200
OK { "id" : 32592054, "nickname" : "ほんだ", "thumbnailUrl" : "http://sp.mbga.jp/img_u/10000/0.0.gif", "profileUrl" : "http://sp.mbga.jp/_u?u=10000", "hasApp" : true } このオブジェクトが リソース。
ROA の統一インターフェイス HTTP メソッドを用いてリソースを取得/更新する。 • GET: リソースの取得 • HEAD: リソースの取得。HTTP
ヘッダのみを返す • POST: リソースの新規作成 • PUT: 既存リソースの置き換え • PATCH: 既存リソースの差分更新 • DELETE: 既存リソースの削除
RPC スタイル • XML-RPC, JSON-RPC など • 一言で言うと、制約が少なく、中央集権型
RPC スタイル • RPC エンドポイント、メソッド名、リクエストパラメータ だけあれば利用できる リクエスト例 POST /rpc-endpoint Content-Type:
application/json { "jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5] }
RPC スタイル • RPC エンドポイント、メソッド名、リクエストパラメータ だけあれば利用できる リクエスト例 POST /rpc-endpoint Content-Type:
application/json { "jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5] } エンドポイントは 1 個しか ない場合が多い リソースの操作方法は HTTP メソッ ドでなくパラメータで指定 基本全部 POST
RPC スタイル • エンドポイントが 1 つしかない場合が多い ◦ リソースごとにユニークな URI を割り当てる
REST と比べると対照的
REST か RPC か • REST ◦ 特徴 ▪ ルール(制約)によって自明なコンセンサスが生まれ、システムを異なる
人が分担して設計したとしても、一定の一貫性を担保できる ◦ 向いているケース ▪ 複数のエンジニアが API の設計をする場合
REST か RPC か • RPC ◦ 特徴 ▪ 柔軟性が高く、中央集権型
◦ 向いているケース ▪ 限定されたエンジニアが設計をする場合
REST か RPC か • RPCは良く言えば自由、悪く言えばバラバラなものができあがりがち • したがって多くの場合でRESTを選択するべき • ネットを見ると「
REST は考えることが多くてだるいよねー、RPC 最高!」という意 見が散見されるが、そう発言する人のほとんどは、REST の「制約によるコンセン サス」というメリットを無視しているので注意
第二章 リソース取得/更新の具体例
エントリリソースの取得 GET /api/1.0.0/users/20000 HTTP/1.1 200 OK { “id”: 100, “nickname:
”nekokak” } 単一のリソースのことを一般的に エントリリソースと呼ぶ 最後の数字が user id 。 URI がリソースを表現する 一意のものになっているのがポイント
コレクションリソースの取得 GET /api/1.0.0/users { items: [ { “id”: 100, “nickname:
”nekokak”}, { “id”: 100, “nickname: ”zigorou”}, ], nextCursor: “1000.1476255989” } 複数リソースのことを一般的に コレクションリソース と呼ぶ 次のページへのポインタ (ページネーション情報)
リソースの新規作成 POST /api/1.0.0/users HTTP/1.1 201 Created { “id”: 100, “nickname:
”zigorou” } (コレクションリソースに対して) 新規のエントリリソースを追加、 という操作なので、主体は users 。 200 OK ではないことに注意 作成されたリソースを返すのが 一般的
エントリリソースの削除 DELETE /api/1.0.0/users/20000 HTTP/1.1 204 No Content 200 OK で返す人が多いが、
レスポンスボディが無いことを 明示的にするために、 204 No Content がベター ※ No Content にするケースが多 いというだけで、Content を返して はいけないというわけではない
リソースの置き換え PUT /api/1.0.0/users/100 Content-Type: application/json; { “id”: 100, “nickname: ”zigorou”
} HTTP/1.1 204 No Conetnt PUT は差分更新でなく まるっと置き換える操作 であることに注意。 差分更新は PATCH 。
リソースの差分更新 PATCH /api/1.0.0/users/100 Content-Type: application/json; [ { “op”: “replace”, “path”:
“/nickname”, “value”: “ねこかく” }, { “op”: “add”, “path”: “/hobby”, “value”: “犬を飼う” } } HTTP/1.1 200 OK { “id”: 100, “nickname”: “ねこかく”, “hobby”: “犬を飼う” } JSON Patch という表現方法。 ・nickname を「ねこかく」に変える ・hobby というフィールドを追加 という差分更新をしている。
JSON PATCH PATCH /api/1.0.0/users/100 Content-Type: application/json; [ { “op”: “replace”,
“path”: “/nickname”, “value”: “ねこかく” }, { “op”: “add”, “path”: “/hobby”, “value”: “犬を飼う” } } ⇒ https://tools.ietf.org/html/rfc6902 op: operation の略。操作を指定。 add, remove, replace, move などの語彙が使える。 path: 更新するフィールドを JSON Pointer という 表現方法で指定。 value: 更新後の値を指定。
第三章 Web API Bad Practice ※ ここから先は、ROA や REST に限らず
Web API 全般の話になります。
悪い例: レスポンスがフラットな配列 • 複数リソースをフラットな配列で返す ◦ 後からページネーション情報とか入れたくなったとき に詰むので、原則 items: みたいなエンベロープで 包む
◦ ページネーション情報などはレスポンスヘッダに含 め、レスポンスボディはリソースだけを返すべきだ、 という宗派もある ▪ 気持ちは分かるけど、ぶっちゃけ使いづらいと 思う GET /api/1.0.0/users HTTP/1.1 200 OK [ { “id”: 100, … }, { “id”: 200, … }, ]
悪い例: /list みたいな URI • GET /api/1.0.0/users/list みたいな URI ◦
ROA において URI は「リソースの場所を表現するもの」であることを思い出す ◦ users が user リソースの集合の意なので、list は蛇足
悪い例: リソースのフィールドをパスに含める • ニックネームの変更で PUT /api/1.0.0/users/nickname?value=nekokak みたいなの ◦ nickname というリソースがあるなら問題ないが、
nickname が user というリソースの単なるフィールドである場合 「 URI はリソースを指し示すもの」という原則から外れる ◦ PUT /api/1.0.0/users ないしは PATCH /api/1.0.0/users で 良い (パラメータで更新フィールドを指定する)
悪い例: 動作を URI に含める • POST /api/1.0.0/notifications/send みたいな URI ◦
くどいが、URI はリソースの場所を表現するもの。 send は行為であってリソースの表現ではない ◦ send する == notification リソースの新規作成する行為 なので、send は不要。 POST /api/1.0.0/notifications で良い。
悪い例: 動作を URI に含める (2) • GET /api/1.0.0/search_users みたいな URI
◦ search は行為であってリソースの表現ではない ◦ /api/1.0.0/users でクエリパラメータで 絞り込み条件を指定できれば OK
悪い例: Limit-Offset のページネーション • ページングが limit-offset 形式になっている ◦ この方法が悪い理由 (1)
▪ たとえば最初の 20 件を取得してから 次の 20 件を取得するまでの間に データの追加/削除があった場合、 実際に取得したい情報と取得した情報にズレが生じる
悪い例: Limit-Offset のページネーション • ページングが limit-offset 形式になっている ◦ この方法が悪い理由 (2)
▪ MySQL などの RDBMS では limit 5 offset 10,000 というクエリを発行した場合、 「 10,005 を取得して最初の 10,000 を捨てる」 という処理が行われる ▪ つまりページが後ろになるほどスロークエリになっていく
悪い例: Limit-Offset のページネーション • ページングが limit-offset 形式になっている ◦ 代わりにどうすべきか ◦
「先頭から数えて何件目」という Pagenation 情報でなく、 「この ID より後のもの」や「この時刻より古いもの」 というページネーション情報を提供するのが吉 ▪ id や created_at にインデックスが貼られている限り、 クエリは高速 ◦ カーソル方式とか呼ばれたりします
悪い例: Limit-Offset のページネーション • ページングが limit-offset 形式になっている ◦ どうしても使いたいときは、 指定できるページ数を制限すべきでしょう。
• 要求に失敗したなら レスポンスは 4XX か 5XX を返すべき POST /api/1.0.0/users HTTP/1.1
200 OK { “success”: false } 悪い例: 要求に失敗してるのに 2XX を返す
悪い例: パスの不要なネスト • ユーザー ID はアプリを横断してユニークなのに /apps/{app_id}/users/{user_id} みたいな URI ◦
/users/{user_id} で良い
第四章 補足
非同期処理時のレスポンスについて • POST/PUT/DELETE などによるリソースの更新処理を 非同期で行う場合は、 202 Accepted というステータスコードが 用意されているのでそれを用いる ◦
201 Created とか 200 OK とか返しちゃ駄目
排他制御処理を行ないたい場合 • レスポンスヘッダの Etag や Last-Modified を用いた Conditional Request (条件付きリクエスト)
という手法を用いるのが一般的。(⇒ RFC7232 ) ◦ いわゆる楽観ロック相当の排他制御をすることが可能 ◦ 話すと長いのでここでの説明は割愛 ◦ 「 Conditional Request 」「条件付きリクエスト」等で 各自ぐぐってくだださい。
Web アプリケーションでも ROA をやりたい • 知ってのとおり、Web のフォームは GET, POST しかサポートしてない
• オーバーロード POST という手法を用いて HTTP Method をオーバーライドする方法がある • POST /books/{book_id}/delete みたいなのを キモいと感じる人は導入すべきでしょう
Web アプリケーションでも ROA をやりたい • オーバーロード POST の例 ◦ Rails
ではフォームの _method パラメータに指定された値に HTTP Method が上書きされる ◦ Perl の Catalyst では x-tunneled-method パラメータに指定された値に HTTP Method が上書きされる
参考
• Web API The Good Parts https://www.amazon.co.jp/dp/4873116864/ • Web を支える技術
https://www.amazon.co.jp/dp/4774142042/ 参考文献
END