Roy FieldingのREST制約(クライアント–サーバ、ステートレス、キャッシュ、均一インターフェース、レイヤード、コードオンデマンドなど)が現代のAPIやWebアプリ設計にどう影響するかを実務的に解説します。

Roy Fieldingは単なるAPIの流行語に名前が付いただけの人物ではありません。彼はHTTPやURI仕様の主要な寄稿者の一人であり、博士論文の中で**REST(Representational State Transfer)**というアーキテクチャスタイルを記述して、なぜWebがこれほどうまく機能しているのかを説明しました。
この起源は重要です。RESTは「見た目の良いエンドポイント」を作るために発明されたわけではありません。世界中の乱雑なネットワークがスケールすることを可能にする制約群を説明する方法として生まれました:多数のクライアント、多数のサーバ、仲介者、キャッシュ、部分的な障害、継続的な変化にも耐えることです。
「REST API」と呼ばれるものがなぜ全く異なる感触になるのか、あるいは小さな設計判断がなぜ後でページネーション地獄やキャッシュの混乱、破壊的変更につながるのか疑問に思ったことがあるなら、このガイドはその驚きを減らすためのものです。
読後には次のものが得られます:
RESTはチェックリストやプロトコル、認証制度ではありません。Fieldingはそれをアーキテクチャスタイルと位置付けました:一緒に適用されるとWebのようにスケールするシステムを生む制約の集合です—使いやすく、時間とともに進化でき、仲介者(プロキシ、キャッシュ、ゲートウェイ)と調整なしに友好的に働くようなシステムです。
初期のWebは多くの組織、サーバ、ネットワーク、クライアントタイプで動作しなければなりませんでした。中央管理なしに成長し、部分的な障害に耐え、新しい機能を導入しても古いものを壊さないようにする必要がありました。RESTは識別子、表現、標準操作のような少数の広く共有される概念を好み、カスタムで密結合な契約を避けることでそれに対処します。
制約とは、利益と引き換えに設計の自由度を制限するルールです。たとえばサーバ側のセッション状態を放棄すれば、どのサーバノードでもリクエストを処理できるようになり、信頼性とスケーリングが向上します。各RESTの制約は同様のトレードオフを行います:任意性を減らす代わりに予測可能性と進化性を得るのです。
多くのHTTP APIはRESTの考え方(HTTP上のJSON、URLエンドポイント、ステータスコードの利用)を借用していますが、制約の全セットを適用しているわけではありません。それは必ずしも「間違い」ではなく、製品納期や内部利用の事情を反映することが多いです。違いを名前で呼べるようにしておくことは有用です:APIは完全にRESTでなくてもリソース指向であり得ます。
RESTシステムを「URLで名前付けできるリソース(もの)を、クライアントが表現(JSONやHTMLのようなその時点のビュー)を通じて操作し、リンク(次のアクションや関連リソース)に導かれてナビゲートする仕組み」と考えてください。クライアントは秘密の外部ルールを必要とせず、リンクに従って動く—ブラウザがWeb上を移動するのと同じです。
制約やHTTPの詳細に迷い込む前に、RESTはシンプルな語彙の転換から始まります:アクションではなくリソースで考えることです。
リソースはあなたのシステム内でアドレス可能な「もの」です:ユーザー、請求書、商品カテゴリ、ショッピングカート。重要なのはそれが身元を持つ名詞であることです。
だから /users/123 は自然に読めます:ID 123 のユーザーを識別しているからです。/getUser や /updateUserPassword のようなアクション形URLと比べてみてください。後者は動詞を表しており、操作そのものを記述しています。
RESTはアクションを禁じてはいません。アクションは均一インターフェース(HTTP APIであれば通常 GET/POST/PUT/PATCH/DELETE のようなメソッド)を通してリソース識別子に対して表現で表現するべきだと言っています。
表現はワイヤ上で送る、その時点のリソースのスナップショットやビューです。同じリソースは複数の表現を持てます。
たとえばリソース /users/123 はアプリ向けのJSONとして表現されることも、ブラウザ向けのHTMLとして表現されることもあります。
GET /users/123
Accept: application/json
は次のような応答を返すかもしれません:
{
"id": 123,
"name": "Asha",
"email": "[email protected]"
}
一方で:
GET /users/123
Accept: text/html
は同じユーザー情報をレンダリングしたHTMLページを返すかもしれません。
重要な点:リソースはJSONではなく、HTMLでもない。それらは単に表現形式に過ぎません。
リソースと表現を中心にAPIをモデル化すると、実務上の判断がいくつか容易になります:
/users/123 はUIやワークフロー、データモデルが進化しても有効であり続ける。このリソース優先の考え方がREST制約の土台です。これがないと「REST」は単なる「HTTP上のJSONと良いURLパターン」になりがちです。
クライアント–サーバの分離は、責任の明確な分割を強制するRESTのやり方です。クライアントはユーザー体験に集中し(表示や操作)、サーバはデータ、ルール、永続化に集中します(なにが真で何が許可されるか)。関心事を分離すれば、一方が変わっても他方を全面的に書き直す必要が減ります。
日常的には、クライアントは「プレゼンテーション層」です:画面、ナビゲーション、素早いフィードバックのためのフォーム検証、楽観的UI(新しいコメントを即表示する等)。サーバは「真実の源」です:認証、認可、ビジネスルール、データストレージ、監査、そしてデバイス間で一貫している必要があるものの全て。
実用的なルール:セキュリティ、金銭、権限、共有データの整合性に関わる決定はサーバ側に置くべきです。見た目や体験に影響する決定(レイアウト、ローカルの入力ヒント、読み込み状態)はクライアント側に置きます。
この制約は次の一般的なセットアップに直接マッチします:
クライアント–サーバの分離は「一つのバックエンドで複数のフロントエンド」を現実にするものです。
頻繁にある間違いは、UIのワークフロー状態をサーバに保存すること(例:ユーザーがチェックアウトのどのステップにいるか)です。これはバックエンドを特定の画面フローに結び付け、スケーリングを難しくします。
必要な文脈を各リクエストで送るか、保存されたリソースから導出する設計を好み、サーバはリソースとルールに集中し、特定のUIの進行状況を覚える役割を持たせないようにしましょう。
ステートレスとは、サーバがクライアント間で何も覚えておかず、各リクエストが理解して正しく応答するために必要なすべての情報を持つことを意味します:呼び出し元が誰か、何を望んでいるか、処理に必要な文脈など。
リクエストが独立していると、ロードバランサの背後にあるサーバを増減しても「どのサーバが私のセッションを知っているか」を気にする必要がありません。これによりスケーラビリティと回復力が向上します。
また運用が単純になります。デバッグはしばしば容易で、必要な文脈がリクエスト(とログ)に表示されるため、サーバ側メモリに隠れた状態を探す必要が減ります。
ステートレスAPIは通常、各呼び出しで少し多くのデータを送ります。サーバ側のセッションに依存する代わりに、クライアントが毎回認証情報や文脈を含めます。
またページネーションやマルチステップのチェックアウトのような「状態のある」ユーザフローは明示的に扱う必要があります。RESTはマルチステップ体験を禁じませんが、状態はクライアント側か識別可能で取得可能なサーバ側リソースに置くよう促します。
Authorization: Bearer … ヘッダを含め、任意のサーバが認証できるようにする。create payment のような操作では Idempotency-Key を送ってリトライで重複作業が起きないようにする。X-Correlation-Id のようなヘッダで分散システム中の一連のユーザーアクションを追跡する。ページネーションでは「サーバがページ3を覚えている」ような設計を避け、?cursor=abc や next リンクのような明示的なパラメータを使うことを推奨します。
キャッシュは以前のレスポンスを安全に再利用して、クライアント(や途中の何か)が同じ作業を再実行せずに済むようにすることで、ユーザーの待ち時間を短縮し、サーバの負荷を減らします。正しく行えばAPIの意味を変えずに性能が向上します。
あるレスポンスが「キャッシュ可能」だというのは、一定期間他のリクエストが同じペイロードを受け取っても安全であることを意味します。HTTPでは以下のヘッダでその意図を表します:
Cache-Control:主スイッチ(保持期間、共有キャッシュ可否など)ETag と Last-Modified:検証手段。クライアントが「変わったか?」と問い合わせて安価に 304 Not Modified を得るために使うExpires:古いがまだ見られる方式これは「ブラウザのキャッシュ」以上の話です。プロキシ、CDN、APIゲートウェイ、モバイルアプリもルールが明確ならレスポンスを再利用できます。
良い候補:
通常キャッシュに向かないもの:
キャッシュは後付けの考えではなく、明示的に新鮮さと検証方法を伝えるAPI設計を報いる制約です。
均一インターフェースは単に「読み取りにはGET、作成にはPOSTを使え」というレベルではありません。Fieldingの意図はもっと大きく、クライアントがエンドポイントごとに特殊な知識を必要としないようにAPIを一貫させることです。
リソースの識別:物事を安定した識別子(通常はURL)で名前付けする。例:/orders/123。
表現による操作:クライアントは表現(JSON、HTMLなど)を送ることでリソースを変更する。サーバがリソースを管理し、クライアントは表現を交換する。
自己記述的メッセージ:各リクエスト/レスポンスは処理方法を理解するのに十分な情報を持つべきだ(メソッド、ステータスコード、ヘッダ、メディアタイプ、明確なボディ)。意味が外部ドキュメントに隠れているとクライアントは密結合になる。
ハイパーメディア(HATEOAS):レスポンスはリンクと実行可能なアクションを含み、クライアントがすべき次の操作をハードコーディングせずにたどれるようにする。
一貫したインターフェースはクライアントがサーバの内部詳細に依存しにくくします。時間が経てば破壊的変更や特例が減り、チームがエンドポイントを進化させても再作業が少なくなります。
200、作成は 201(Locationを含める)、バリデーションは 400、認証/認可は 401/403、リソース未存在は 404。code、message、details、requestId。Content-Type、キャッシュヘッダなど)。均一インターフェースは予測可能性と進化性に関するものであり、単なる「正しい動詞の使用」以上の概念です。
自己記述的メッセージとは、受信側がそれをどう解釈するかを外部の部族的知識(Wikiや口伝)なしで知れるメッセージです。クライアントや仲介者がヘッダとボディを見ただけで意味がわからないなら、あなたはHTTPの上にプライベートなプロトコルを作ってしまっています。
単純な勝利は Content-Type(送っているもの)と Accept(望むもの)を明示することです。Content-Type: application/json は基本的なパースルールを伝えますが、意味が重要な場合はベンダーメディアタイプやプロファイルを使うこともできます。
アプローチの例:
application/json による慎重に維持されたスキーマ。大多数のチームにとって最も簡単。application/vnd.acme.invoice+json のように特定の表現を示す。application/json を維持しつつ profile パラメータやリンクで意味を定義する。バージョニングは既存クライアントを保護するために使います。一般的な選択肢:
/v1/orders):分かりやすいが表現のフォークを促すことがあるAccept 経由):URLを安定化させ、意味をメッセージに含める何を選ぶにせよ後方互換性をデフォルトにすることを目指してください:フィールドの名前を軽々しく変えない、意味を密かに変更しない、削除は破壊的変更として扱う。
エラーがどこでも同じ形をしているとクライアントは学びやすくなります。1つのエラー形(例:code、message、details、traceId)を選んでエンドポイント全体で使い、フィールド名のスタイル(createdAt vs created_at)も統一しましょう。
良いドキュメントは採用を加速しますが、意味が唯一そこにあるべきではありません。クライアントが status: 2 が「支払い済み」か「保留」かをWikiを読まないと理解できないなら、メッセージは自己記述的ではありません。よく設計されたヘッダ、メディアタイプ、読みやすいペイロードはその依存を減らし、システムの進化を容易にします。
ハイパーメディア(HATEOAS)はクライアントが事前にAPIの次のURLを知る必要がないように、レスポンスに発見可能な次のステップをリンクとして含めることを意味します:次にどこへ行くか、どのアクションが可能か、時にはどのHTTPメソッドを使うかまで。
クライアントが /orders/{id}/cancel のようなパスをハードコーディングする代わりに、サーバがレスポンスでリンクを提供します。サーバは「このリソースの現在の状態に応じて有効な動きはこれです」と伝えるわけです。
{
"id": "ord_123",
"status": "pending",
"total": 49.90,
"_links": {
"self": { "href": "/orders/ord_123" },
"payment":{ "href": "/orders/ord_123/payment", "method": "POST" },
"cancel": { "href": "/orders/ord_123", "method": "DELETE" }
}
}
注文が paid になればサーバは cancel を含めなくなり refund を追加するかもしれません。そうすれば適切に実装されたクライアントは壊れずに済みます。
フローが進化する場面(オンボーディング、チェックアウト、承認、サブスクリプションなど)で特に有効です。「次に許可されること」が状態や権限、ビジネスルールで変わる場合、サーバがリンクで示すことでクライアントを堅牢にできます。
リンク形式の定義、リレーション名の合意、クライアント開発者にリンクを辿る実装を教えるなど、導入に手間がかかるためチームはしばしば省略します。
省略すると失うのは重要なRESTの利点:疎結合です。ハイパーメディアがないと、多くのAPIは「HTTP上のRPC」になり、クライアントはドキュメントと固定URLテンプレートに強く依存するようになります。
レイヤードシステムとは、クライアントがその要求を「本物の」オリジンサーバが処理したのか、途中の仲介者が処理したのかを知る必要がない(しばしば知れない)ことを意味します。これらのレイヤにはAPIゲートウェイ、リバースプロキシ、CDN、認証サービス、WAF、サービスメッシュ、マイクロサービス間の内部ルーティングなどが含まれます。
レイヤは境界を作ります。セキュリティチームはTLS、レート制限、認証、リクエスト検証をエッジで強制でき、すべてのバックエンドサービスを変更する必要がありません。運用チームはゲートウェイの背後に水平スケールを追加したり、CDNでキャッシュしたり、インシデント時にトラフィックをシフトしたりできます。クライアントにとっては安定したAPIエンドポイント、整合されたヘッダ、予測可能なエラーフォーマットが得られます。
仲介者は隠れたレイテンシ(追加のホップやハンドシェイク)を導入し、デバッグを難しくすることがあります:バグはゲートウェイルール、CDNキャッシュ、オリジンコードのいずれかにあるかもしれません。キャッシュは異なるレイヤが別々にキャッシュすると混乱を生み、ゲートウェイがキャッシュキーに影響するヘッダを書き換えると問題になります。
レイヤはシステムが可観測で予測可能である限り強力な道具です。
コードオンデマンドはREST制約のうち明確に任意なものです。サーバがクライアントに実行可能なコードを送り、クライアント側で実行して機能を拡張できるという考え方です。クライアント側にすべての振る舞いをあらかじめ詰め込む代わりに、必要に応じて新しいロジックをダウンロードできます。
フォーム検証、チャート描画、テーブルのフィルタリングなどのためにページ読み込み後にJavaScriptが配信されて動的に振る舞いを提供する、という経験は誰もがしています。サーバはHTMLやデータに加え、ブラウザで実行されるJavaScriptを届けることで、クライアントを一般目的のものに保ちながら素早く機能を配信できます。
RESTはコードオンデマンドなしでも十分に機能します。他の制約だけでスケーラビリティ、シンプルさ、相互運用性を実現できます。さらに多くのモダンなWeb APIは実行可能コードを送らない傾向にあります。理由は:
あなたがクライアント環境を制御していてUI振る舞いを素早くロールアウトしたい場合、あるいは薄いクライアントがサーバから「プラグイン」やルールをダウンロードするような設計では有用です。しかしこれはあくまで追加のツールであり、必須ではありません。
要点:RESTはコードオンデマンドなしでも完全に満たせる。多くの本番APIはこの制約を採用していません。
ほとんどのチームはRESTを完全に否定するわけではなく、「RESTっぽい」スタイルを採りつつ重要な制約を落としていきます。これは問題になることもあれば合理的な選択であることもあります。大事なのは意図的なトレードオフかどうかです。
繰り返し現れるパターン:
/doThing、/runReport、/users/activate—名前付けが楽で配線しやすい。/createOrder、/updateProfile、/deleteItem—HTTPメソッドが二次的になる。これらは初期には生産的に見えますが、内部的な関数名や業務操作をそのまま反映しているためです。
APIがどの程度REST的かをレビューするための指針:
/orders/{id} を優先する。Cache-Control、ETag、Vary を定義する。REST制約は理論だけでなく、出荷時に感じるガードレールです。例えばReactフロントエンドとGo + PostgreSQLバックエンドをスキャフォールディングして素早くAPIを出すとき、最も簡単な実装に流されてインターフェースが決まってしまうのがよくあるミスです。
Koder.aiのようなチャットからウェブアプリを構築するプラットフォームを使う場合でも、これらのREST制約を早い段階で会話に含めておくと、迅速な反復でもクライアントにとって予測可能で進化しやすいAPIが得られます(Koder.aiはソースコードのエクスポートをサポートするので、API契約や実装を要件の変化に合わせて洗練できます)。
主要なリソースをまず定義し、次に制約を意図的に選んでください:キャッシュやハイパーメディアをスキップする場合はその理由と代替手段をドキュメント化しましょう。目標は純粋性ではなく明快さです:安定したリソース識別子、予測可能な意味論、そしてシステムが進化してもクライアントが耐えられるような明確なトレードオフを維持することです。
REST(Representational State Transfer)は、Webがスケールする理由を説明するためにRoy Fieldingが提示したアーキテクチャスタイルです。
これはプロトコルや認証制度ではなく、いくつかの制約(クライアント–サーバ、ステートレス性、キャッシュ可能性、均一インターフェース、レイヤードシステム、任意のコードオンデマンド)をまとめたもので、柔軟性の一部を犠牲にする代わりにスケーラビリティ、進化性、相互運用性を得ることが目的です。
多くのAPIは一部のREST的な考え(HTTP上のJSON、分かりやすいURL、ステータスコードの使用など)は取り入れている一方で、キャッシュ規則やハイパーメディアのような他の制約を無視することがよくあります。
そのため、2つの“REST API”が大きく異なって感じられるのは、次の点で差が出るからです:
リソースは識別できる名詞です(例:/users/123)。アクションエンドポイントはURLに動詞を埋め込んだものです(例:/getUser、/updatePassword)。
リソース指向の設計は、識別子が安定するため長期的に扱いやすく、UIやワークフローの変化に強くなります。動詞ベースのエンドポイントはそのままアクションとして残すこともできますが、通常はHTTPメソッドと表現を通じて表現するのが望ましいです。
リソースは概念(「ユーザー123」)であり、表現はそのリソースを転送するスナップショット(JSON、HTMLなど)です。
重要なのはリソースがJSONではない点です。異なる表現を追加・変更してもリソース識別子を維持できるため、クライアントはリソースの意味に依存し、特定のペイロード形式に依存しない設計が可能になります。
クライアント–サーバの分離は関心事の分離を促進します。
セキュリティや金銭、権限、共有データの整合性に関わる決定はサーバ側で行うべきで、これにより「1つのバックエンドで複数のフロントエンド」が現実的になります。
ステートレスとは、サーバがクライアント間で状態を保持せず、各リクエストが処理に必要な全情報を含むことを意味します。これにより任意のサーバノードでリクエストを処理でき、水平スケールや復元力が向上します。
実務例:
Authorization: Bearer … を送るIdempotency-Key を使う?cursor=... や リンクで行い、「サーバがページ3を覚えている」ような設計を避けるキャッシュは以前のレスポンスを安全に再利用することで、レイテンシを下げ、サーバ負荷を軽減します。HTTPではキャッシュヘッダでその意図を示します。
主なツール:
Cache-Control(新鮮さや共有可否)ETag / Last-Modified(検証用、304 Not Modified を可能にする)均一インターフェースは単に GET/POST/PUT/DELETE を正しく使うこと以上を意味します。クライアントがエンドポイントごとの特殊ルールを知らなくて済む、予測可能で一貫したインターフェースを提供することが目的です。
注目すべき点:
/orders/123)実務では、ステータスコードの一貫性、標準化されたエラーフォーマット、意味のあるメディアタイプとヘッダを守ることが重要です。
ハイパーメディア(HATEOAS)は、クライアントが事前に次のURLを知っている必要がないように、レスポンスに『次にできること』をリンクとして含める考え方です。
例:
レイヤードシステムとは、クライアントが実際にどのコンポーネント(オリジンサーバやゲートウェイ、CDN、プロキシなど)と通信しているかを意識しなくてよい設計です。これによりセキュリティやスケーリング、キャッシングをエッジやゲートウェイで一元的に施行できます。
実務上の注意点:
レイヤを活かすにはシステムの可観測性と予測可能性が重要です。
コードオンデマンドはRESTの制約のうち唯一「任意」のもので、サーバがクライアントに実行可能なコードを送ってクライアントを拡張することを指します。
最も馴染みのある例はブラウザでのJavaScriptです。サーバがHTMLとデータに加えてJavaScriptを配信し、クライアントで振る舞いを提供します。しかし多くのAPIはセキュリティやポリシー、監査の複雑さのためコードオンデマンドを避けます。
コードオンデマンドは管理されたクライアント環境や薄いクライアントにプラグイン的に機能を追加したい場合に有効ですが、RESTを満たすために必須ではありません。
多くのチームはRESTを丸ごと放棄するのではなく「REST-ish(RESTっぽい)」なスタイルを採用し、重要な制約を落としてしまうことがあります。これは製品スピードの都合で合理的なこともありますが、無意識に行うと後で問題になります。
よくあるショートカット:
/doThing、/runReport)/createOrderなど)その結果として起きやすい問題:
nextExpires実務ルール:公開され全員で同一のデータや変更頻度の低い読み取り専用リソースは積極的にキャッシュし、ユーザー固有データや認証に関わる応答は慎重に扱う(多くは private か非キャッシュ)。
{
"id": "ord_123",
"status": "pending",
"total": 49.90,
"_links": {
"self": { "href": "/orders/ord_123" },
"payment":{ "href": "/orders/ord_123/payment", "method": "POST" },
"cancel": { "href": "/orders/ord_123", "method": "DELETE" }
}
}
状態が paid になればサーバは cancel を外して refund を追加するかもしれません。フローが状態や権限で変化する領域(チェックアウト、オンボーディング、承認など)では特に有効ですが、定義と採用の手間がかかるため見送られることが多いです。
ハイパーメディアを使わないと、APIは「HTTP上のRPC」のようになり、ドキュメントや固定パスに強く依存するクライアントが生まれやすくなります。
実用的なチェックリスト:
/orders/{id} を優先)Cache-Control, ETag, Vary)Koder.aiのような高速プロトタイピングプラットフォームでアプリを作る場合でも、早い段階でこれらの制約を会話に入れておくと、迅速な反復でも予測可能で進化しやすいAPIが作りやすくなります。
要点:主要なリソースを定義し、どの制約を採用しどれを意図的に省くかを明確にしましょう。目的は純度ではなく明確さです。安定したリソース識別子、予測可能な意味論、およびシステムが進化してもクライアントが堅牢でいられるような明示的なトレードオフを保つことが目標です。