あかね
API設計の原則と落とし穴: リソースとクエリで崩れない設計とは
2026年02月22日
要約を生成中...
はじめに
先日、こんな質問を受けました(内容は一部調整しています)。
一覧データ取得APIについて
・特定日のみ取得
・1ヶ月分取得(最大31件)
・今日から7日・1ヶ月・半年などの可変期間
画面ごとに取得条件が違うため、/month/[month] や /range/[range] のようにエンドポイントを分けようと思っていますが、問題ないでしょうか?
この質問を受けて、難しいよね!わかんないよね!!!!って思いました!
これは「実装の話」ではなく「設計の話」だからです。当然悩むはず。
よくある設計パターン
つい、こう設計したくなります。
GET /records/[id]
GET /records/month/[month]
GET /records/range/[range]
UIごとにAPIを分ける発想です。
一見わかりやすいですが、ここで立ち止まりたいポイントがあります。
原則:パスは「リソース」、クエリは「条件」
HTTP設計の基本はシンプルです。
パス = 何を扱うか(リソース)
クエリ = どう絞るか(条件)
今回扱っているのはすべて「records」という同じリソース。
違うのは「取得条件」だけです。
つまり設計として自然なのは、
GET /records?start=20260101&end=20260228という形になります。
recordsというリソースを「条件付きで取得」している、という考え方です。
なぜエンドポイントを分けないほうがいいのか
/month や /range を作ると、こうなります。
/range/7days
/range/30days
/range/halfyear
/range/custom
どんどん増殖します。
UIの都合がそのままAPI設計に漏れ始めます。APIは「画面単位」で増やすものではありません。
「リソース単位」で設計するものです。
さらに、取得だけでなく更新や削除も「リソース単位」で行われます。
もし取得時に /month や /range など画面都合でエンドポイントを分けてしまうと、更新時に「これはrecordsの更新なのか、それともmonthの更新なのか?、はたまたどっちも??」という設計の歪みが生まれます。
リソースは常に一貫しているべきです。取得条件が変わるだけで、扱っている対象そのものが変わるわけではありません。
条件の正規化という考え方
設計を安定させるコツは、条件を正規化することです。
例えばすべてを start と end に統一します。
特定日 → start=20260101&end=20260102
1ヶ月 → start=20260201&end=20260301
7日間 → start=今日&end=今日+7日
UIで「7日」「1ヶ月」というボタンがあっても、 APIは常に start と end で受け取る。
これが「UI都合をAPIに漏らさない」設計です。
キャッシュ効率の話(中級者向け)
もう一段踏み込むと、「エンドポイントを一つにまとめる」ことは クライアント側のキャッシュ戦略をシンプルにする、というメリットもあります。
例えば、
/records/month/2026-02
/records/range/7days
/records/range/30days
のようにエンドポイントを分けると、 クライアント側では「どのURLをキーにキャッシュするか」を個別に管理する必要があります。
一方で
GET /records?start=20260201&end=20260301のように統一されていれば、 キャッシュキーは「/records + クエリ文字列」という一貫した構造になります。
SWR のようなデータフェッチライブラリでも、キーの設計がシンプルになることは、再利用がしやすい、無駄な再取得を防ぎやすいといったメリットがあります。
また、CDNやHTTPキャッシュの観点でも、
GETであること
URLが一意であること
は大きな意味を持ちます。API設計は「サーバー側の都合」だけではありません。
クライアント・キャッシュ・将来の拡張性まで含めて考えられると、設計は一段強くなります。
GETとPOSTの話
検索が複雑になりすぎた場合は、 POSTで検索専用エンドポイントを作るケースもあります。
例:
POST /records/_searchただし今回のような期間指定程度であれば、 GET + クエリパラメータで十分です。
原則を崩すのは「必要になったとき」です。
正直データ取得したいのにPOSTを使用することには違和感も感じますが、設計的になしではない(条件次第で)ということもわかって驚きでした!
