/note/tech

Web API設計の現在地2026 #HTTP

要約:

■ 1. 記事の狙いと背景

  • Web API設計情報の陳腐化:
    • Web API設計を調べると検索上位の記事が2015〜2019年あたりで止まっている
    • その間にエラーレスポンスの標準ができ、OAuthのグラントタイプは選択基準が変わった
    • APIの廃止告知にまでRFCが生えた
  • 一次情報での現在地確認:
    • Web API設計の主要領域ごとに2026年時点で従うべきものを一次情報で確認した結果をまとめる
    • 一次情報とはRFC、IETFのドラフト、大手APIの実装を指す
  • 執筆のきっかけ:
    • 2014年の『Web API: The Good Parts』にあった「仕様に従う、仕様がなければデファクトに従う」という指針
    • その仕様とデファクトが今どこにあるのかが気になった

■ 2. 全体マップ

  • エラーレスポンス:
    • Problem Details形式で返す、根拠は2023年に標準化されたRFC 9457
  • 日時フォーマット:
    • 2026-08-07T12:34:56Z 形式で返す、根拠は20年以上変わらず現役のRFC 3339
  • メソッド・ステータスコード:
    • 迷ったらRFC 9110を引く、9110は2022年に旧2616/7231を統合した文書
  • 認証・認可:
    • Authorization Code + PKCEを使い、ログイン用途はOIDCを重ねる
    • 根拠は2025年のRFC 9700(BCP 240)で、内容はOAuth 2.1ドラフトへ統合中
  • バージョニング:
    • 基本はパスに v1、不特定多数向け公開APIなら日付ベースを検討する
    • 標準はなく、Google AIP-185とGitHub/Stripeの実装が根拠となる
  • 廃止告知:
    • Deprecation / Sunsetヘッダで機械可読に伝える、根拠はRFC 9745(2025年)とRFC 8594(2019年)
  • ページネーション:
    • カーソル方式にする、標準はなくGitHub/Stripeの実装がデファクトとなる
  • レートリミット:
    • 429と X-RateLimit-* 系ヘッダを返す
    • 429はRFC 6585が根拠、残量ヘッダは標準がなく標準化が進行中
  • 冪等キー:
    • Idempotency-Key ヘッダを受け付ける、標準化は停滞中でStripeの実装がデファクトとなる
  • API記述:
    • OpenAPIで書く、Linux Foundation傘下のOpenAPI Initiativeが仕様を管理し現行は3.2.0

■ 3. エラーレスポンス: RFC 9457 Problem Details

  • 最も大きく変わった領域:
    • かつてエラーレスポンス形式は各サービスが独自に設計するものだった
    • { "error": { "code": 123, "message": "..." } } 型やエラー配列型など複数の流派が生まれた
    • 流派が乱立したのは単に標準が存在しなかったからにすぎない
    • 2016年のRFC 7807を初出とし、2023年のRFC 9457でこの状況は終わっている
  • レスポンスの形:
    • Content-Typeは application/problem+json を用いる
    • ボディは typetitlestatusdetailinstance の各フィールドを持つ
  • 2つのレイヤーの両立:
    • HTTPステータスコードとアプリ固有のエラー識別子が1つのボディで両立する
    • status にトランスポート層のカテゴリ、type に具体的に何が起きたかのURIが入る
    • この2つは別レイヤーの情報であり、どちらか片方では足りない
  • 拡張フィールドの公認:
    • バリデーションエラーのようにフィールド単位の詳細が要る場合、errors 配列などの独自フィールドを足してよい
  • フレームワーク対応状況の言語圏差:
    • Spring Framework 6 / Boot 3は ProblemDetail クラスを標準搭載する
      • 組み込み例外の自動problem+json化は spring.mvc.problemdetails.enabled=true によるopt-inとなる
    • ASP.NET Coreは [ApiController] のエラーを ProblemDetails へ自動変換し、デフォルトで有効になる
      • Minimal APIは.NET 7から AddProblemDetails() を呼ぶ必要がある
    • NestJSは組み込み対応がなく、既定は { statusCode, message } 形式で、寄せるなら例外フィルタで自前実装する
    • Express / Fastify / Honoは組み込み対応がなく、既定のエラー形式はそれぞれ独自となる
  • 設計者に残る判断:
    • JavaやC#の世界では標準がフレームワークに入り始めている
    • JS/TS系のフレームワークにはまだ入っていない
    • JS/TSでAPIを書くなら、RFC 9457に寄せるかどうかも含めエラー形式の決定は依然として設計者の仕事となる

■ 4. 日時フォーマット: RFC 3339のまま

  • 20年変わらない標準:
    • 日時は今もRFC 3339の 2026-08-07T12:34:56Z に従う
    • UTCで Z を付けて返し、表示時にクライアント側でローカライズする分担も含め20年以上変わっていない
  • ISO 8601との関係:
    • RFC 3339はISO 8601から曖昧さを排した実用プロファイルという位置づけとなる
    • Web APIで使うのはRFC 3339である

■ 5. メソッド・ステータスコード: 正典はRFC 9110

  • HTTP仕様の再編:
    • HTTPそのものの仕様は2022年に再編された
    • RFC 2616(1999年)とその後継のRFC 7230番台(2014年)は置き換わった
    • 統合先はRFC 9110(HTTP Semantics)、9111(Caching)、9112(HTTP/1.1)である
  • 現行の正典:
    • メソッドとステータスコードの意味論の現行の正典は9110である
    • 標準としての格もInternet Standardに上がった
  • 実務での使い方:
    • ステータスコードの選択に迷ったら9110の該当セクションを引く
    • 「400番台はクライアント起因、500番台はサーバ起因」という原則の出典もここにある
  • 429の例外:
    • APIで頻出のコードのうち 429 Too Many Requests は例外となる
    • 出典は追加ステータスコードを定義したRFC 6585(2012年)である
  • 資料の鮮度の目印:
    • RFC 2616を引いている解説は、HTTP仕様が2世代前だった時点の記事だと分かる

■ 6. 認証・認可: 変わったのはグラントタイプの選択基準

  • 骨格は不変:
    • 現行の標準はOAuth 2.0(RFC 6749、2012年)である
    • 骨格となる4つのロールやトークンの考え方は当時から変わっていない
  • 選択基準の変化:
    • グラントタイプの選択基準が大きく変わり、古い記事を信じると事故る箇所となる
    • SPAはかつてImplicitグラントを使ったが、現在は非推奨でAuthorization Code + PKCEを使う
    • ID/パスワードを直接預かるROPCは使用禁止(MUST NOT)となった
    • PKCEはかつてモバイル向けの追加対策だったが、パブリッククライアントでは必須となり、それ以外にも推奨される
  • RFC 9700という根拠:
    • RFC 9700「Best Current Practice for OAuth 2.0 Security」は2025年1月に発行されBCP 240となる
    • ROPCをMUST NOT、Implicitを条件付きのSHOULD NOTと規定する
    • パブリッククライアントにPKCEを必須とし、認可サーバ側のPKCEサポートも必須とする
  • OAuth 2.1の位置づけ:
    • OAuth 2.1はRFC 9700の内容をOAuth 2.0本体の仕様へ統合し直しているドラフトにすぎない
    • 2020年から議論が続き2026年8月時点でrev 15、まだRFCではない
    • 2.1のRFC化を待つ必要はなく、従うべき文書は既に発行されている
  • 認可と認証の区別:
    • OAuth 2.0は認可の仕組みであって認証ではない
    • ログインに使うならOpenID Connectが上に乗る

■ 7. バージョニング: パスの v1 と日付ベースの二大流派

  • 標準の不在:
    • バージョニングに標準仕様はなく、完全にデファクトの世界となる
    • 主要サービスを実際に叩いて現在の方式を確認した
  • 主要サービスの実測:
    • GitHubは日付+ヘッダ方式で x-github-api-version-selected: 2022-11-28 を返す
    • Stripeは日付+ヘッダ方式で 2026-07-29.dahlia を返す
    • Shopifyは日付をパスに置き、2026-04 のように四半期ごとに刻む
    • GoogleはメジャーバージョンをパスにおきAIP-185で v1 を必須と規定する
    • Microsoft Graphは /v1.0/ /beta/ とメジャー番号をパスに置く
    • Azureは日付をクエリに置き ?api-version=2023-01-01 とする
  • 二大流派の分布:
    • パス方式が数では多数派のままとなる
    • 不特定多数の外部開発者を抱える公開API側のGitHub、Stripe、Shopifyが日付ベースに寄っている
    • GitHubは昔の Accept: application/vnd.github.v3+json から2022年に日付ヘッダ方式へ移った点で象徴的となる
  • 選択の判断軸:
    • v1 → v2 の一括切り替えは全ユーザーの同時大移動を要求するため現実には起きず、v1が永遠に残る
    • 日付ベースは変更を小さく刻んで各ユーザーが自分のペースで上がるモデルとなる
    • 日付ベースではサーバ側が変換層で差分を吸収するため、そのコストを払えるかで選ぶ
    • クライアントを自分で掌握しているかも判断軸となり、社内・自社アプリ向けならパス方式の弱点は出ない

■ 8. APIの終わらせ方: DeprecationとSunsetヘッダ

  • 語られない終わらせ方:
    • バージョニングの記事は世の中に山ほどあるが、その続きにある終わらせ方はほとんど書かれていない
    • 実際にはこの領域も標準化されている
  • 2つのヘッダ:
    • Deprecationヘッダ(RFC 9745、2025年)が「このAPIは非推奨です」を伝える
    • Sunsetヘッダ(RFC 8594、2019年)が「この日時に停止します」を伝える
    • Deprecationは「いつから非推奨か」をUnixタイムスタンプで持つ
    • Sunsetは「いつ停止するか」をHTTP日付形式で持つ
    • Link ヘッダに rel="deprecation" を付けて移行ドキュメントも案内できる
  • 通知先が変わる価値:
    • 廃止告知をブログとメールでやっても、そのAPIを実際に叩いているコードには届かない
    • レスポンスヘッダに載せればクライアント側のログと監視にそのまま残る
    • 通知先が開発者のメールボックスからクライアントの実行ログに変わる点がこの2つのヘッダの価値となる
    • 廃止予定のエンドポイントを抱えているなら、次の廃止からすぐ使える

■ 9. ページネーション: カーソル方式が主流

  • デファクトの結論:
    • ページネーションにも標準仕様はないが、デファクトの答えは固まっている
    • 大量データや無限スクロールならカーソル方式(絶対位置指定)を選ぶ
  • オフセット方式の2つの問題:
    • ?page=3?offset=100&limit=20 のオフセット方式は後半のページほど遅くなる
      • OFFSET 100000 はDBが10万件を実際に読んでから捨てる動きとなり、深いページほど線形に重くなる
    • もうひとつの問題はズレることである
      • ページ1を見ている間に新しいデータが1件挿入されると全体が1つずれる
      • 結果としてページ2で同じレコードを二重に見たり、逆に読み飛ばしたりする
  • カーソル方式の利点:
    • 「最後に見たID」を基準に WHERE id < :cursor ORDER BY id DESC LIMIT 20 で取得する
    • インデックスで直接その位置に飛べるため何ページ目でも一定速度となる
    • 途中の挿入・削除でもズレない
  • カーソル方式のトレードオフ:
    • 「5ページ目に飛ぶ」ができない
    • ページ番号ジャンプが要る管理画面ならオフセット方式にも出番が残る
  • 次ページの伝え方:
    • GitHubの Link: <...>; rel="next" が参考になる
    • 方式そのものに標準はないが、伝え方の部品には標準があり Linkrel="next" はRFC 8288(Web Linking)で定義される
    • HATEOAS(レスポンスに次のアクションのリンクを含める設計思想)は全面採用こそ普及しなかった
    • HATEOASはこの rel="next" という形で部分的に生き残っている
  • 後戻りの難しさ:
    • ページネーション方式はAPIの外部仕様であり、後から変えると全クライアントの改修になる
    • 最初にどちらか決めておきたい領域となる

■ 10. レートリミットと冪等キー: 標準化が実装を追いかける領域

  • 逆転した状態:
    • この2領域は実運用のデファクトが先に固まり、標準化が後から追いかけている
  • レートリミットで決まっていること:
    • 超過時に 429 Too Many Requests を返す(RFC 6585、2012年)
    • 回復までの時間を Retry-After で伝える
  • レートリミットの残量ヘッダ:
    • 残量を伝えるヘッダには標準がない
    • X-RateLimit-Limit / X-RateLimit-Remaining / X-RateLimit-Reset という X- 付きの形が各社デファクトとして定着している
  • IETFドラフトの中身:
    • httpapiワーキンググループが標準化を進め、2026年8月時点でrev 11 / Activeのドラフトがある
    • ドラフトが定義するのは既存デファクトの X- を取った3ヘッダ形式ではない
    • X- プレフィックスという慣行自体がRFC 6648(2012年)で新規採用を非推奨とされている
    • そのためデファクトをそのまま追認する標準化にはなりようがなかった
    • ドラフトが定義するのは RateLimit-Policy(制限のルール)と RateLimit(現在の残量)の2つとなる
    • 形式はStructured Fieldsを使い RateLimit-Policy: "default";q=100;w=10 のように書く
  • 新旧の意味論の差:
    • q が割り当て量、w が時間窓(秒)、r が残量を表す
    • t は実効ウィンドウと呼ばれ、「この先t秒間はrを超えて使えない」という制約を表す
    • X-RateLimit-Reset のような「リセットまでの残り秒数」ではなく、t秒後に全量が回復する保証はない
    • 値は次のレスポンスで変わりうると仕様に明記されている
  • 現時点の実装方針:
    • 標準化が完了しても X-RateLimit-* からの乗り換えは機械的な改名では済まない
    • 今実装するなら X-RateLimit-* 系のデファクトに合わせ、ドラフトの完成を待って対応を判断するのが現実的となる
  • 冪等キーの標準化停滞:
    • Idempotency-Key ヘッダの標準化ドラフトはrev 07が2026年4月に期限切れ(Expired)となった
    • 標準化のプロセス自体が止まっている
    • それでも冪等キーは決済系APIの必須機構として現役で、Stripeのドキュメントが事実上の仕様書として参照され続けている
  • Stripe仕様の要点:
    • 対象はPOSTのみとなる
    • キーにはv4 UUIDなど十分ランダムな文字列を使い、最大255文字とする
    • 同じキーの再送には最初のリクエストの結果(ステータスコードとボディ)を成功・失敗を問わずそのまま返す
    • 同じキーで異なるパラメータを送るとエラーとなる
    • キーは24時間経過後に削除される可能性があり、削除後の再利用は新規リクエストとして処理される
    • 恒久的な重複排除ではなく、リトライを安全にするための短期の仕組みとして設計されている
    • 各社の実装もおおむねこの形に倣っている
  • 実装が仕様書になる領域:
    • 標準化が追いつかない領域では、よくできた実装が仕様書の代わりになる
    • RFCを探して見つからなくても諦めず、大手APIのリファレンスというデファクトの現物を探しに行く価値がある

■ 11. API記述: OpenAPI 3.2.0

  • 現行バージョン:
    • API仕様の記述形式はOpenAPIがデファクトで、現行の最新は3.2.0(2025年9月)となる
    • 「Swagger」の名前で覚えている場合はバージョン2.0の世界で止まっているため、名前ごと更新しておきたい
    • Swagger 2.0がOpenAPI Initiativeに寄贈されてOpenAPIになった
  • デファクトと言える根拠:
    • 仕様を管理するOpenAPI InitiativeはLinux Foundation傘下のベンダー中立な組織である
    • Google、Microsoft、IBM、Bloomberg、SAP、Salesforceなどが参画している
    • 提供する側ではGitHubとStripeが自社APIの公式OpenAPI記述をリポジトリで配布している
    • フレームワーク側ではFastAPIがOpenAPI生成を設計の中核に組み込んでいる
    • ASP.NET Coreも.NET 9以降はテンプレート標準で生成を持ち、Javaはコミュニティ製のspringdoc-openapiが定番となる
    • 採用率を示す独立の調査データは見つからず何%という話はできない
    • それでも作る側と配る側の両方でここまで土台になっている形式は他にない
  • 位置づけの変化:
    • かつてのSwaggerはAPIドキュメントを綺麗に表示するものだった
    • 今のOpenAPIはスキーマから型付きクライアントやサーバスタブを生成する起点となる
    • ドキュメントはその副産物という扱いに近い
  • データ形式の現在地:
    • 公開Web APIはJSON固定が主流になった
    • かつて紹介された「Accept ヘッダでJSONとXMLを選ばせる」設計は、仕様をシンプルに保つ方向に負けて廃れた
    • 形式の多様性は別の場所に移り、用途で分化した
      • 社内のサービス間通信はgRPC/Protocol Buffersが担う
      • クライアント主導でフィールドを選ばせたい場面はGraphQLが担う

■ 12. 標準がない領域の調べ方

  • 参照先の使い分け:
    • RFCで決まっている領域は素直にRFCに従えばよい
    • 問題はバージョニングやページネーションのような標準がない領域となる
  • 企業のAPI設計ガイド:
    • 体系性ならGoogle AIP(API Improvement Proposals)が優れる
      • 番号付きのルール集で、バージョニングの v1 必須(AIP-185)のように個別に引ける
    • 読み物として優れているのはZalando RESTful API Guidelinesで、なぜそうするかの理由が丁寧に書かれている
    • 古株のGoogle JSON Style Guideもリポジトリを見ると2025年に社内版から同期されており、放置ドキュメントではなく現役である
  • 新しい標準の定点観測先:
    • IETFのhttpapiワーキンググループを見る
    • レートリミットや冪等キーのようなWeb API周りの標準化はここに集まる
  • 調べる順番:
    • まずRFCがあるかをRFC Editorで確認する
    • なければ標準化が進行中かをIETF Datatrackerで確認する
    • それも無ければGoogle AIPやGitHub / Stripeのような大手APIの実装というデファクトの現物を見る
    • この記事の各領域は、この手順を2026年8月時点で一巡した結果となる