この記事の最終更新日: 2026年6月18日

REST APIを設計していると、検索APIをGETで作るべきか、POSTで作るべきか悩むことがあります。
単純な検索であれば、次のようにGETで十分です。
GET /users?name=tanaka&status=active
しかし、検索条件が増えてくると話が少し変わります。
たとえば、次のような条件がある場合です。
- 複数のステータス
- 日付範囲
- 複数キーワード
- AND / OR 条件
- 除外条件
- 並び順
- ページネーション
- 複雑なフィルター
- 配列やネストした条件
- 権限や表示項目の指定
このような場合、GETのクエリパラメータだけで表現すると、URLが長くなったり、読みづらくなったりします。
では、検索条件が多い場合はGETとPOSTのどちらを使うべきなのでしょうか。
この記事では、REST APIで検索条件が多い場合のGETとPOSTの使い分けを、初心者にもわかりやすく解説します。
まず結論
結論から言うと、基本方針は次のようになります。
単純な検索はGET。検索条件が複雑でURLに載せるのがつらい場合はPOSTも選択肢になる。
もう少し実務寄りに言うと、次のような判断になります。
| 条件 | 推奨 |
|---|---|
| 条件が少ない | GET |
| URLで自然に表現できる | GET |
| ブックマーク・共有したい | GET |
| キャッシュを効かせたい | GET |
| 条件が多くURLが長くなる | POST |
| JSONで複雑な条件を送りたい | POST |
| AND / OR / ネスト条件が多い | POST |
| URLに検索条件を残したくない | POST |
つまり、検索だから必ずGETでも、条件が多いから必ずPOSTでもありません。
APIの用途、検索条件の複雑さ、キャッシュ、ログ、セキュリティ、実装のしやすさを見て判断するのが現実的です。
GETとは何か
GETは、リソースを取得するためのHTTPメソッドです。
検索APIでは、よく次のように使います。
GET /articles?keyword=api
ユーザー一覧を検索する場合は、次のようになります。
GET /users?status=active&role=admin
GETの特徴は、検索条件をURLのクエリパラメータに含めることです。
/users?status=active&role=admin
この形式は、シンプルな検索にはとても向いています。
POSTとは何か
POSTは、サーバーにデータを送信するためのHTTPメソッドです。
一般的には、新規作成や処理の実行で使われます。
POST /users
Content-Type: application/json
{
"name": "Tanaka",
"email": "tanaka@example.com"
}
ただし、POSTは必ずしも「作成専用」ではありません。
検索条件をリクエストボディに入れて送る設計もあります。
POST /users/search
Content-Type: application/json
{
"status": ["active", "pending"],
"roles": ["admin", "editor"],
"created_from": "2025-01-01",
"created_to": "2025-12-31"
}
このように、複雑な検索条件をJSONで表現したい場合、POSTが使われることがあります。
REST的には検索はGETが基本
REST APIでは、検索や一覧取得は基本的にGETで表現することが多いです。
たとえば、ユーザー一覧を取得するAPIなら次のようになります。
GET /users
条件を付ける場合は、クエリパラメータを使います。
GET /users?status=active
複数条件でも、シンプルならGETで問題ありません。
GET /users?status=active&role=admin&page=1&per_page=20
このようなAPIはわかりやすく、REST APIとしても自然です。
GETのメリット
GETには、検索APIとして使いやすいメリットがあります。
1. URLで検索条件が見える
GETでは、検索条件がURLに含まれます。
GET /users?status=active&role=admin
そのため、どの条件で検索しているのかがわかりやすいです。
ブラウザや開発者ツールで見ても、内容を確認しやすいです。
2. ブックマーク・共有しやすい
GETの検索条件はURLに含まれるため、そのURLをそのまま共有できます。
管理画面や検索画面では、これは大きなメリットです。
たとえば、検索結果画面をチームメンバーに共有したい場合、GETならURLを送るだけで済みます。
3. キャッシュしやすい
GETは、HTTPキャッシュやCDNとの相性がよいです。
同じURLに対するリクエストであれば、キャッシュを活用しやすくなります。
GET /articles?category=tech&page=1
検索結果をキャッシュしたい場合、GETの方が扱いやすいです。
4. APIの意図がわかりやすい
GETは「取得する」という意味が明確です。
検索APIをGETで表現すると、
このAPIはデータを取得するだけで、サーバーの状態を変更しない
という意図が伝わりやすくなります。
GETのデメリット
一方で、GETには限界もあります。
1. URLが長くなりやすい
検索条件が増えると、URLが非常に長くなります。
GET /users?status=active&status=pending&role=admin&role=editor&created_from=2025-01-01&created_to=2025-12-31&keyword=tanaka&sort=created_at&order=desc&page=1&per_page=50
この程度ならまだ読めますが、条件がさらに増えるとつらくなります。
特に、配列やネストした条件、AND / OR 条件が入ると、URLがかなり複雑になります。
2. 複雑な条件を表現しにくい
たとえば、次のような検索条件を考えます。
status が active または pending
かつ
role が admin または editor
かつ
created_at が 2025-01-01 以降
かつ
name または email に "tanaka" を含む
これをGETのクエリパラメータで表現すると、設計が難しくなります。
GET /users?status[]=active&status[]=pending&role[]=admin&role[]=editor&created_from=2025-01-01&q=tanaka
さらにOR条件や除外条件が増えると、クエリパラメータだけでは読みづらくなります。
3. URLに残したくない条件がある
GETでは検索条件がURLに出ます。
そのため、ブラウザ履歴、アクセスログ、プロキシログ、解析ツールなどに残りやすいです。
検索条件に個人情報や機微な情報が含まれる場合、GETでURLに載せるのは避けた方がよいことがあります。
たとえば、次のような条件です。
メールアドレス
電話番号
住所
個人名
社内ID
機密性の高いキーワード
もちろんPOSTにすれば完全に安全というわけではありません。
ただ、URLに露出させたくない条件がある場合は、POSTの方が扱いやすいことがあります。
4. URL長の制限に引っかかることがある
HTTP仕様そのものというより、ブラウザ、サーバー、プロキシ、ロードバランサーなどの実装上、URL長には制限があります。
検索条件が多すぎると、URLが長くなりすぎてエラーになることがあります。
たとえば、次のようなケースです。
- IDの配列を大量に渡す
- 複雑なフィルターをURLエンコードして渡す
- 長いキーワードを複数渡す
- JSONをクエリパラメータに詰め込む
このような場合は、GETよりPOSTの方が現実的です。
POSTで検索するメリット
検索APIでPOSTを使うと、リクエストボディにJSONを渡せます。
POST /users/search
Content-Type: application/json
{
"statuses": ["active", "pending"],
"roles": ["admin", "editor"],
"created_at": {
"from": "2025-01-01",
"to": "2025-12-31"
},
"keyword": "tanaka",
"sort": {
"field": "created_at",
"order": "desc"
},
"pagination": {
"page": 1,
"per_page": 50
}
}
このように、複雑な検索条件を構造化して表現できます。
1. 複雑な検索条件を表現しやすい
POSTではJSONを使えるため、ネストした条件や配列を自然に表現できます。
たとえば、条件グループを作ることもできます。
{
"and": [
{
"field": "status",
"operator": "in",
"value": ["active", "pending"]
},
{
"or": [
{
"field": "name",
"operator": "contains",
"value": "tanaka"
},
{
"field": "email",
"operator": "contains",
"value": "tanaka"
}
]
}
]
}
これをGETのクエリパラメータで表現するのはかなり大変です。
複雑な検索UIを持つ業務システムでは、POST検索の方が扱いやすいことがあります。
2. URLが長くならない
POSTでは検索条件をリクエストボディに入れるため、URLは短く保てます。
POST /users/search
URLに長い検索条件を詰め込む必要がありません。
そのため、URL長の制限を気にしにくくなります。
3. フロントエンドとバックエンドで型を合わせやすい
検索条件が複雑な場合、フロントエンド側でも検索条件をオブジェクトとして持つことが多いです。
type UserSearchCondition = {
statuses: string[];
roles: string[];
createdAt: {
from?: string;
to?: string;
};
keyword?: string;
page: number;
perPage: number;
};
このオブジェクトをそのままJSONとしてPOSTできると、実装がシンプルになります。
await fetch("/users/search", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(condition)
});
複雑な条件をクエリ文字列に変換する処理を書かなくて済みます。
4. 検索条件の拡張に強い
検索条件は、後から増えやすいです。
最初は次の程度だったとしても、
keyword
status
page
後から次のような条件が増えることがあります。
role
created_at
updated_at
tags
exclude_ids
has_attachment
assigned_user_ids
sort
display_fields
POSTでJSONにしておくと、条件追加が比較的しやすくなります。
POSTで検索するデメリット
POST検索にもデメリットがあります。
1. RESTとして違和感を持たれることがある
検索はデータ取得なので、本来はGETの方が自然です。
そのため、POSTで検索APIを作ると、
検索なのにPOSTなの?
と感じる人もいます。
特にREST APIの原則を重視するチームでは、POST検索に違和感が出ることがあります。
2. キャッシュしにくい
GETに比べると、POSTはHTTPキャッシュやCDNの扱いが難しくなりがちです。
同じ検索条件でも、リクエストボディの内容まで含めてキャッシュを管理する必要があります。
検索結果をキャッシュしたい場合は、POSTよりGETの方が扱いやすいです。
3. URL共有しにくい
POST検索では、検索条件がURLに含まれません。
そのため、検索結果ページのURLを共有しても、同じ条件を再現できない場合があります。
管理画面などで検索条件の共有が重要な場合は、POSTだけだと不便です。
この場合は、フロントエンド側で検索条件をURLに保持する設計も検討します。
4. 「安全な操作」だと伝わりにくい
GETは、基本的にサーバーの状態を変更しない操作として理解されます。
一方、POSTは作成や更新にも使われるため、検索にPOSTを使うと、API利用者が少し身構える場合があります。
そのため、POST検索を使う場合は、API仕様書で
このAPIは検索専用であり、データを変更しない
と明記するとよいです。
実務での判断基準
では、実務ではどう判断すればよいのでしょうか。
おすすめは、次の基準です。
基本はGETで設計する
検索条件が少なく、URLで自然に表現できるならGETを使います。
GET /users?status=active&role=admin&page=1&per_page=20
このようなAPIは、わかりやすく、RESTらしく、キャッシュや共有にも向いています。
条件が複雑ならPOSTを検討する
次のような場合は、POST検索を検討してよいです。
- 条件が多すぎる
- 配列条件が多い
- ネストした条件がある
- AND / OR 条件を表現したい
- URLが長くなりすぎる
- 検索条件をJSONで扱いたい
- URLに条件を出したくない
- 業務システムで高度な検索画面がある
この場合は、次のようなAPIが現実的です。
POST /users/search
Content-Type: application/json
{
"statuses": ["active", "pending"],
"roles": ["admin", "editor"],
"keyword": "tanaka",
"created_at": {
"from": "2025-01-01",
"to": "2025-12-31"
},
"page": 1,
"per_page": 50
}
GETとPOSTの使い分け例
実務では、次のように使い分けるとわかりやすいです。
| APIの内容 | 推奨 |
|---|---|
| 一覧取得 | GET |
| 簡単な検索 | GET |
| ページネーション付き一覧 | GET |
| ソート付き一覧 | GET |
| 複数条件の簡単な検索 | GET |
| 複雑な詳細検索 | POST |
| 条件グループを含む検索 | POST |
| 大量のIDリストを条件にする検索 | POST |
| 検索条件を保存する | POST |
| 検索結果をエクスポートする | POSTも検討 |
URL設計の例
GETの場合は、リソースに対する検索として自然に表現します。
GET /users?keyword=tanaka&status=active
記事検索なら次のようになります。
GET /articles?keyword=rest&tag=api&page=1
POSTの場合は、検索専用のエンドポイントを用意することが多いです。
POST /users/search
または、検索をリソースとして扱う設計もあります。
POST /user-searches
ただし、単に検索結果を返すだけなら、実務では /search を使うケースが多いです。
POST検索はREST違反なのか?
POSTで検索することを「REST違反」と言い切る必要はありません。
たしかに、単純な取得や検索はGETが自然です。
しかし、検索条件が非常に複雑な場合、GETだけにこだわるとAPIが使いにくくなることがあります。
重要なのは、HTTPメソッドの意味を理解したうえで、利用者にとってわかりやすく、安全で、運用しやすいAPIにすることです。
特に業務システムでは、複雑な検索条件を扱うことが多いため、POST検索は実務上よくある設計です。
GETで無理に複雑な条件を表現する例
たとえば、次のようなURLを考えてみます。
GET /users?filter=%7B%22and%22%3A%5B%7B%22field%22%3A%22status%22%2C%22operator%22%3A%22in%22%2C%22value%22%3A%5B%22active%22%2C%22pending%22%5D%7D%5D%7D
これは、JSONをURLエンコードしてクエリパラメータに入れている例です。
技術的には可能ですが、かなり読みづらいです。
このような状態になるなら、POSTでJSONを送った方が自然です。
POST /users/search
{
"and": [
{
"field": "status",
"operator": "in",
"value": ["active", "pending"]
}
]
}
APIは機械が読むものですが、人間が設計・保守するものでもあります。
読みづらすぎるURLは、デバッグや運用の負担になります。
ページネーションとソートはどうする?
GETでもPOSTでも、ページネーションやソートの扱いは重要です。
GETなら次のようにします。
GET /users?status=active&page=1&per_page=50&sort=created_at&order=desc
POSTならリクエストボディに含めます。
{
"status": "active",
"pagination": {
"page": 1,
"per_page": 50
},
"sort": {
"field": "created_at",
"order": "desc"
}
}
どちらの場合でも、レスポンスにはページ情報を含めると親切です。
{
"data": [
{
"id": 1,
"name": "Tanaka"
}
],
"pagination": {
"page": 1,
"per_page": 50,
"total": 128
}
}
検索条件をURLに残したい場合
管理画面や検索画面では、検索条件をURLに残したいことがあります。
たとえば、次のような理由です。
- ブラウザバックで検索状態を復元したい
- URLを共有したい
- リロードしても条件を保持したい
- ブックマークしたい
この場合は、APIがPOSTでも、フロントエンド側では検索条件をURLに持たせる設計があります。
たとえば、画面URLは次のようにします。
/admin/users?status=active&role=admin&page=1
しかし、APIリクエストはPOSTにします。
POST /api/users/search
このように、画面のURL設計とAPIのHTTPメソッド設計は分けて考えることもできます。
セキュリティ面の注意
GETかPOSTかに関係なく、検索APIではセキュリティに注意が必要です。
特に、次の点は重要です。
- 認可されていないデータを返さない
- ユーザーが指定できるsort項目を制限する
- SQLインジェクション対策をする
- 大量検索による負荷を防ぐ
- page / per_page の上限を設ける
- LIKE検索や全文検索の負荷を考慮する
- 個人情報をURLに載せすぎない
- ログに残る情報を意識する
POSTにしたから安全、GETだから危険、という単純な話ではありません。
API側で適切にバリデーション、認可、レート制限、ログ設計を行う必要があります。
よくある設計パターン
パターン1:シンプルな一覧検索はGET
GET /users?keyword=tanaka&status=active&page=1
この形式は、最も一般的でわかりやすいです。
パターン2:詳細検索はPOST
POST /users/search
Content-Type: application/json
{
"keyword": "tanaka",
"statuses": ["active", "pending"],
"roles": ["admin", "editor"],
"created_at": {
"from": "2025-01-01",
"to": "2025-12-31"
},
"page": 1,
"per_page": 50
}
検索条件が多い業務システムでは、この形式が扱いやすいです。
パターン3:GETとPOSTを併用する
シンプル検索はGET、詳細検索はPOSTに分ける方法もあります。
GET /users?keyword=tanaka
POST /users/search
ただし、似た検索APIが複数あると混乱しやすいため、使い分けのルールを明確にする必要があります。
パターン4:検索条件を保存する
検索条件そのものを保存したい場合は、検索条件をリソースとして扱う設計もあります。
POST /saved-searches
{
"name": "アクティブな管理者ユーザー",
"conditions": {
"statuses": ["active"],
"roles": ["admin"]
}
}
その後、保存した検索条件を使って実行します。
GET /saved-searches/123/results
これは高度な管理画面や分析画面で使われることがあります。
実務でおすすめの方針
実務では、次の方針がわかりやすいです。
1. まずGETで表現できるか考える
2. URLが自然で短いならGET
3. 条件が複雑ならPOST検索を検討する
4. POST検索にする場合は、検索専用APIであることを明確にする
5. API仕様書に「データは変更しない」と明記する
たとえば、次のような基準をチームで決めておくとよいです。
基本:
GET /resources?key=value
詳細検索:
POST /resources/search
検索条件がJSONでネストする場合:
POSTを許可
検索結果を共有したい画面:
画面URLには検索条件を保持する
このようにルール化しておくと、API設計のブレを減らせます。
GETとPOSTの判断チェックリスト
最後に、判断用のチェックリストを整理します。
GETを選びやすいケース
- 検索条件が少ない
- URLが短く読みやすい
- ブックマークしたい
- URL共有したい
- HTTPキャッシュを活かしたい
- RESTらしさを重視したい
- 一般的な一覧取得である
例:
GET /products?category=book&keyword=api&page=1
POSTを選びやすいケース
- 検索条件が多い
- 配列やネスト条件が多い
- AND / OR 条件を表現したい
- URLが長すぎる
- JSONで条件を送りたい
- URLに条件を出したくない
- 高度な検索画面である
- GETだと保守しづらい
例:
POST /products/search
{
"categories": ["book", "ebook"],
"keywords": ["api", "rest"],
"price": {
"min": 1000,
"max": 5000
},
"conditions": {
"operator": "and"
}
}
まとめ
REST APIで検索条件が多い場合、GETとPOSTのどちらを使うべきかは、単純に決められるものではありません。
基本はGETです。
GET /users?status=active&role=admin
GETは、検索条件がURLで見え、共有しやすく、キャッシュとも相性がよいです。
一方で、検索条件が多い場合や、複雑な条件を扱う場合はPOSTも有力な選択肢になります。
POST /users/search
{
"statuses": ["active", "pending"],
"roles": ["admin", "editor"],
"created_at": {
"from": "2025-01-01",
"to": "2025-12-31"
}
}
重要なのは、次の考え方です。
単純な検索はGET。複雑な検索はPOSTも許容する。
そして、POSTで検索する場合は、API仕様書で
このAPIは検索専用であり、データを変更しない
と明確にしておくとよいです。
REST APIの設計では、原則を理解したうえで、実務上の使いやすさ、保守性、セキュリティ、キャッシュ、URL共有の必要性をバランスよく判断することが大切です。

大阪のエンジニアが書いているブログ。



コメント