Web●●●○○

REST API

REST API ・ れすとえーぴーあい

資源を URL で名指しし、意味の決まった HTTP メソッドで操作する API の設計様式。キャッシュや再送が素直に効く。

✦ 30秒で試す — 航海応答が消えたとき、送り直していい要求とダメな要求があるこの直後にメモ7を GET すると、返るのは?予想してみる

概要

REST APIは、「ユーザー一覧は GET /users、新規作成は POST /users、42番のユーザーは GET /users/42」のように、操作対象(リソース)をURLで名指しし、操作の種類をHTTPメソッドで表すAPIの設計スタイルです。RESTはRepresentational State Transferの略で、もともとはHTTP仕様の策定にも関わったRoy Fieldingが2000年の博士論文で示したアーキテクチャ原則の名前でした。

新しいプロトコルを発明するのではなく、Webがすでに持っている語彙——URL、HTTPメソッド、ステータスコード——をそのまま設計に活かすのが特徴です。レスポンスの形式にはJSONを使うのが現代の慣習で、「HTTP + JSON + リソース指向のURL」という組み合わせは、Web APIの事実上の標準になっています。

なぜ生まれたか

2000年代初頭、システム間連携の本命とされていたのはSOAPというプロトコルでした。XMLで封筒(エンベロープ)を作り、WSDLという仕様記述言語で契約を定義する重厚な仕組みで、ツールなしには読み書きが困難なほど複雑でした。一方でWebそのものは、シンプルなHTTPとURLだけで惑星規模に成長していました。しかも SOAP はあらゆる呼び出しを同じ URL への POST に詰め込むので、途中のキャッシュやプロキシから見ると中身の分からない書き込みの連続にしか見えず、Web が育ててきた仕組みがほとんど効きません。「この成功したアーキテクチャの原則を、API設計にもそのまま使えばいいのではないか」——Fieldingの論文はWebがなぜスケールしたのかを分析したものであり、RESTはその原則に付けられた名前です。

決定打になったのは学習コストの低さです。RESTふうのAPIは、URLとメソッドを見れば何をするのか推測でき、ブラウザやcurlだけで試せます。2000年代後半、TwitterやAmazonなどがREST形式のAPIを公開してエコシステムを築くと、SOAPは急速に廃れ、「Web APIといえばREST」という時代が訪れました。

詳細

リソースと動詞の対応

RESTの中心にあるのは「名詞はURL、動詞はHTTPメソッド」という分担です。ユーザーというリソースに対するCRUD操作(作成・読み取り・更新・削除)は、次のように4つのメソッドに素直に対応します。

HTTPメソッド(動詞)+ URL(名詞)CRUD操作POST/users作成CreateGET/users/42取得ReadPUT / PATCH/users/42更新UpdateDELETE/users/42削除Delete
HTTPメソッドとCRUD操作の対応。URLが名詞(リソース)を、メソッドが動詞(操作)を担う

結果の報告にはHTTPステータスコードを流用します。成功なら200番台(作成成功は201)、リクエスト側の誤りなら400番台(未認証は401、認可されていない=権限不足は403、リソース不在は404)、サーバ側の障害なら500番台。独自のエラー体系を発明する代わりに、Webの共通語彙に乗ることで、汎用のツールやキャッシュ、監視の仕組みがそのまま活きます。

メソッドの約束 — 安全と冪等

メソッドは単なるラベルではなく、HTTP の仕様(RFC 9110)が性質を約束しています。安全(safe)なメソッドは、サーバの状態を変えないと約束したもので、GET と HEAD がこれに当たります。冪等(idempotent)なメソッドは、同じ要求を1回送っても何回送ってもサーバの状態が同じになると約束したもので、安全なメソッドに加えて PUT と DELETE が含まれます。PUT は「/users/42 をこの内容にする」、DELETE は「/users/42 を無くす」という、結果の状態を指定する要求だからです。一方 POST は「このコレクションに新しく1つ足す」なので、2回送れば2つできます。POST は安全でも冪等でもありません(PATCH も一般には冪等ではありません)。

この約束は、クライアントとサーバの間にいる装置やライブラリが中身を知らなくても正しく振る舞うための手がかりです。GET が安全だからこそ、ブラウザや CDN は応答を覚えて使い回せます(逆に PUT・POST・DELETE の応答が通ると、キャッシュはその URL の保存分を捨てます)。PUT と DELETE が冪等だからこそ、応答が途中で消えたときに、HTTP クライアントは「サーバが処理したかどうか分からない」まま安全に再送できます。POST ではそうはいかず、黙って再送すると二重注文や二重決済になるので、多くのクライアントは POST を自動では再送しません。どうしても再送したい POST には、要求に一意なキーを付けてサーバ側で重複を捨てる、冪等性キーの仕組みを使います。

サーバクライアントサーバクライアントメモ7を置き換えるタイムアウト。処理されたか分からない同じ内容で置き換えるだけPOST なら、ここで2件目が作られていたPUT /notes/7 内容は牛乳を2本200 OK が途中で消える同じ PUT を再送200 OK

GET・PUT・POST・DELETE を同じメモに送って確かめてください。クライアントと CDN の間の応答の線を押すと、応答が1回だけ途中で消える回線になります。

⚡ 体験: メソッドの約束が、中間の仕組みを支える
送る
要求 →要求 →← 応答← 応答(押すと回線が不安定に)クライアント自動で再送するCDNGET を覚えるオリジンメモを持つCDN のキャッシュ空サーバのメモ(1 件)#7 牛乳GET安全冪等PUT—冪等POST—冪等でないDELETE—冪等メソッドの約束
まだ何も送っていません
○ GET が CDN で折り返す○ PUT を2回送っても1件のまま○ POST を2回送ると2件できる○ 応答が落ちても PUT の再送は無害○ 応答が落ちると POST の再送で二重に

上のボタンで、同じメモ /notes/7 に要求を送ってください。まずは GET を2回。次に PUT を2回と POST を2回送り比べ、最後にクライアントと CDN の間の応答の線を押して回線を不安定にしてから、もう一度。

単純化しています。実際の HTTP クライアントの多くは、応答が来ないときに自動で再送するのは冪等なメソッドだけで、POST は再送しません(再送するなら Idempotency-Key のような仕組みで二重処理を防ぎます)。ここでは違いを見るために、どのメソッドも再送させています。

ステートレス性という規律

RESTの重要な原則に「ステートレスであること」があります。各リクエストは、それ単体で処理に必要な情報をすべて含んでいなければならず、サーバは「さっきのリクエストの続き」といった会話の文脈を持ちません。この規律のおかげで、どのリクエストをどのサーバが処理しても結果が同じになり、ロードバランサで台数を増やすスケーリングが素直に効きます。認証情報をセッションではなく、毎回のリクエストに添えるトークン(JWTなど)で運ぶ設計が好まれるのも、この原則の延長線上にあります。

「RESTっぽさ」の程度問題

Fieldingの論文が定めた原則を厳密に満たすAPIは、実はほとんど存在しません。特にHATEOAS(レスポンスに次に辿れる操作のリンクを含め、クライアントがそれを頼りに遷移する原則)まで実装したAPIは稀です。実務で「REST API」と呼ばれているものの大半は、「リソース指向のURL + HTTPメソッド + JSON」という慣習を押さえた「RESTっぽいAPI」です。成熟度を段階で示すリチャードソン成熟度モデルという物差しもありますが、現場では厳密さより一貫性——命名規則、複数形の統一、エラー形式の統一——のほうがずっと重要です。

設計の勘所と典型的な悩みどころ

実際に設計を始めると、教科書どおりにいかない場面にすぐ出会います。「検索」や「ログイン」のようにリソースに素直に写像できない操作をどうURLにするか。ネストしたリソース(/users/42/posts)をどこまで深くするか。部分更新はPUTかPATCHか。一覧APIのページネーションやフィルタをクエリパラメータでどう表すか。これらに唯一の正解はなく、チームで規約を決めて一貫させることが答えになります。仕様はOpenAPI(Swagger)でスキーマとして記述し、ドキュメントとクライアントコードを自動生成するのが現代の定石です。

RESTの限界とその後継たち

RESTには構造的な弱点もあります。画面に必要なデータが複数リソースにまたがると何往復もリクエストが必要になり(アンダーフェッチ)、逆に固定のレスポンス形式では不要なフィールドまで受け取ってしまいます(オーバーフェッチ)。この課題への応答がGraphQLで、クライアントが「欲しいデータの形」をクエリで指定します。またマイクロサービス間の内部通信のように性能と型の厳密さが優先される場面では、スキーマからコードを生成してバイナリ形式で通信するgRPCが選ばれます。RESTが消えたわけではなく、HTTP の意味論に乗っているからこそキャッシュや再送、監視の道具がそのまま効くという強みは今も健在で、「外向けの公開APIはREST、内部通信はgRPC、複雑なフロントエンド向けはGraphQL」のような適材適所の併存が現在の風景です。

試問

クライアント・CDN・サーバが並び、ボタンで GET /notes/7・PUT /notes/7(メモ7を「牛乳を2本」にする)・POST /notes(新しいメモを1つ足す)・DELETE /notes/7 を送れます。サーバのメモはいま空で、回線はどの要求も、サーバが処理した後に応答だけが1回途中で消え、クライアントが自動で同じ要求を再送します。

✦ 命題挑戦中

応答が1回途中で消える回線で、空のサーバを、メモがちょうど1件だけある状態にせよ

  • 回線は応答が1回消えるまま(要求は再送される)
  • サーバのメモがちょうど1件
⚡ 体験: メソッドの約束が、中間の仕組みを支える
送る
要求 →要求 →← 応答応答が1回落ちるクライアント自動で再送するCDNGET を覚えるオリジンメモを持つCDN のキャッシュ空サーバのメモ(0 件)(空)GET安全冪等PUT—冪等POST—冪等でないDELETE—冪等メソッドの約束
まだ何も送っていません

サーバのメモは空です。回線は、どの要求も応答だけが1回途中で消え、クライアントが自動で再送します。上のボタンで要求を送ってください。

単純化しています。実際の HTTP クライアントの多くは、応答が来ないときに自動で再送するのは冪等なメソッドだけで、POST は再送しません(再送するなら Idempotency-Key のような仕組みで二重処理を防ぎます)。ここでは違いを見るために、どのメソッドも再送させています。