- API
- 技術 💻
REST APIの設計|URL・ステータスコード・エラー形式の決め方
REST APIの設計とは、「どんなURLに、どのHTTPメソッドでリクエストを送ると、どんな結果が返るか」を決めることです。URLには名詞(リソース)を使い、操作は GET・POST・PUT・PATCH・DELETE で表し、結果はステータスコードで伝えるのが基本です。
基本のルールはシンプルですが、実務では「エラーはどんな形で返すか」「一覧は何件ずつ返すか」「同じリクエストが2回届いたらどうするか」など、決めておかないと、画面側の実装やチーム内で迷う点がたくさんあります。この記事では、実際に動かした注文APIとcurlの実行結果を使って、それぞれの決め方を解説します。最後に、設計レビューで使えるチェックリストも紹介します。
REST API設計の早見表
| 項目 | 基本のルール | 例 |
|---|---|---|
| URL | 名詞・複数形・小文字、単語はハイフンでつなぐ | /v1/orders、/v1/order-items |
| 1件の指定 | IDをパスに入れる | /v1/orders/123 |
| 取得 | GET | GET /v1/orders |
| 作成 | POST | POST /v1/orders → 201 Created |
| 全体の置き換え | PUT | PUT /v1/orders/123 |
| 一部の更新 | PATCH | PATCH /v1/orders/123 |
| 削除 | DELETE | DELETE /v1/orders/123 → 204 No Content |
| 絞り込み・並べ替え | クエリパラメータ | ?status=paid&sort=-created_at |
| エラーの形式 | RFC 9457(Problem Details) | application/problem+json |
| バージョン | URLに入れるのがわかりやすい | /v1/... |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「REST APIの設計|URL・ステータスコード・エラー形式の決め方」
URLの付け方
名詞を使い、操作はHTTPメソッドで表す
| 避けたい書き方 | おすすめの書き方 |
|---|---|
POST /createOrder |
POST /v1/orders |
GET /getOrderList |
GET /v1/orders |
POST /deleteOrder?id=123 |
DELETE /v1/orders/123 |
GET /v1/order/123 |
GET /v1/orders/123(コレクションは複数形) |
関係は1〜2階層まで
GET /v1/customers/42/orders # 顧客42の注文一覧(わかりやすい)
GET /v1/customers/42/orders/7/items/3/options # 深すぎる
深くなりすぎる場合は、GET /v1/order-items/3 のように、そのリソース単独のURLを用意します。
「動詞」がどうしても必要な操作
「注文をキャンセルする」「パスワードを再設定する」のように、単純な作成・更新・削除に当てはまらない操作もあります。その場合は、次のどちらかでチーム内で統一します。
| 方法 | 例 |
|---|---|
| 状態の更新として表す | PATCH /v1/orders/123 に {"status": "cancelled"} |
| 操作をサブリソースとして表す | POST /v1/orders/123/cancel |
キャンセルに理由や返金の処理が伴う場合は、後者の方が意図が明確になります。
HTTPメソッドの使い分け
| メソッド | 用途 | 冪等性 | 成功時のステータス |
|---|---|---|---|
| GET | 取得 | あり | 200 OK |
| POST | 作成、または冪等でない操作 | なし | 201 Created(作成)、202 Accepted(受け付けのみ) |
| PUT | 全体の置き換え | あり | 200 OK または 204 No Content |
| PATCH | 一部の更新 | 保証されない | 200 OK |
| DELETE | 削除 | あり | 204 No Content |
冪等性(べきとうせい)とは、「同じリクエストを何回送っても、結果が同じになる」性質です。PUTで「数量を3にする」は何回送っても3ですが、POSTで「注文を作る」を2回送ると、注文が2件できてしまいます。この違いは、後半の「Idempotency-Key」で対策を紹介します。
ステータスコードの使い分け
| 場面 | ステータスコード |
|---|---|
| 取得・更新に成功 | 200 OK |
| 作成に成功 | 201 Created(Location ヘッダーに新しいURL) |
| 受け付けたが、処理は後で行う | 202 Accepted |
| 成功したが、返す内容がない(削除など) | 204 No Content |
| リクエストの形式が正しくない(JSONが壊れているなど) | 400 Bad Request |
| 認証されていない(ログインしていない) | 401 Unauthorized |
| 権限がない | 403 Forbidden |
| リソースが存在しない | 404 Not Found |
| 状態が合わず処理できない(発送済みの注文を変更など) | 409 Conflict |
| 入力値の検証エラー | 422 Unprocessable Content(400で統一する設計もある) |
| リクエストが多すぎる | 429 Too Many Requests |
| サーバー側の不具合 | 500 Internal Server Error |
| 一時的に利用できない | 503 Service Unavailable |
大切なのは、エラーなのに200を返さないことです。200 OK で {"result": "error"} を返す設計にすると、画面側やHTTPのライブラリ、監視の仕組みが「成功」と判断してしまい、エラーの処理が複雑になります。
エラーの形式はRFC 9457に揃える
エラーの形式は、APIごとにばらばらになりがちです。RFC 9457(Problem Details for HTTP APIs)という標準の形式に揃えると、画面側で共通の処理が書けます。
| 項目 | 意味 |
|---|---|
type |
エラーの種類を表すURI |
title |
エラーの種類の短い説明 |
status |
HTTPステータスコード |
detail |
この発生についての具体的な説明 |
instance |
このエラーが発生した場所(リクエストのURLなど) |
必要に応じて、errors のような独自の項目を追加できます。入力の検証エラーでは、どの項目がなぜエラーなのかを返すと、画面側で項目の横にメッセージを表示できます。
実際に動かして確認する
ここからは、Pythonの標準ライブラリだけで作った小さな注文APIに、curlでリクエストを送った結果です。
作成する(201 Created)
curl -i -X POST http://127.0.0.1:8765/v1/orders \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7c1e-01' \
-d '{"item":"マウス","quantity":2}'
HTTP/1.0 201 Created
Location: /v1/orders/2
Content-Type: application/json
{"id": 2, "item": "マウス", "quantity": 2, "status": "pending"}
作成したリソースのURLを Location ヘッダーで返し、本文には作成された内容を返しています。
入力エラー(422)
curl -i -X POST http://127.0.0.1:8765/v1/orders \
-H 'Content-Type: application/json' \
-d '{"quantity":0}'
HTTP/1.0 422 Unprocessable Entity
Content-Type: application/problem+json
{"type": "https://api.example.com/problems/validation-error",
"title": "Validation Error", "status": 422,
"detail": "入力内容に誤りがあります",
"errors": [{"field": "item", "message": "必須です"},
{"field": "quantity", "message": "1以上の整数で指定してください"}]}
エラーを1つずつではなく、まとめて返すと、利用者は1回で全部を直せます。
状態が合わない(409 Conflict)
curl -X PATCH http://127.0.0.1:8765/v1/orders/2 \
-H 'Content-Type: application/json' -d '{"status":"shipped"}'
curl -X PATCH http://127.0.0.1:8765/v1/orders/2 \
-H 'Content-Type: application/json' -d '{"quantity":3}'
{"id": 2, "item": "マウス", "quantity": 2, "status": "shipped"}
{"type": "https://api.example.com/problems/conflict", "title": "Conflict",
"status": 409, "detail": "発送済みの注文は変更できません"}
入力の形式は正しいが、リソースの今の状態では処理できないときは409を返します。
削除(204)と、もう一度削除(404)
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE http://127.0.0.1:8765/v1/orders/1
curl -s -o /dev/null -w "%{http_code}\n" -X DELETE http://127.0.0.1:8765/v1/orders/1
204
404
DELETEは冪等(何回送ってもリソースが消えている状態は同じ)ですが、2回目のステータスコードは404になります。冪等性とは「サーバーの状態が同じになる」ことで、返ってくるステータスコードまで同じとは限りません。
ページング:オフセット方式とカーソル方式
一覧を返すAPIでは、一度に返す件数を制限します。制限がないと、データが増えたときに応答が遅くなり、サーバーの負荷も高くなります。
| 方式 | リクエストの例 | 長所 | 短所 |
|---|---|---|---|
| オフセット方式 | ?limit=20&offset=40 |
実装が簡単。「5ページ目」に直接移動できる | データが多いと遅くなる。途中で追加・削除があると、重複や抜けが出る |
| カーソル方式 | ?limit=20&cursor=eyJpZCI6MTAwfQ |
大量のデータでも速い。途中の追加・削除に強い | 任意のページに直接移動できない |
{
"data": [ { "id": 101, "item": "キーボード" }, "..." ],
"next_cursor": "eyJpZCI6MTIwfQ"
}
管理画面のようにページ番号で移動する画面ではオフセット方式、無限スクロールや、外部連携で全件を取得する場面ではカーソル方式が向いています。どちらの場合も、limit には上限(100件など)を設けます。
絞り込み・並べ替え・項目の選択
| 目的 | 例 |
|---|---|
| 絞り込み | GET /v1/orders?status=paid&customer_id=42 |
| 期間 | GET /v1/orders?created_from=2026-09-01&created_to=2026-09-30 |
| 並べ替え | GET /v1/orders?sort=-created_at(- で降順) |
| 返す項目を選ぶ | GET /v1/orders?fields=id,status,total |
パラメータの名前の付け方(created_from か from か、sort=-created_at か order=desc か)は、API全体で統一します。
バージョン管理
| 方法 | 例 | 特徴 |
|---|---|---|
| URL | /v1/orders |
わかりやすく、ブラウザやログでも確認しやすい。最も一般的 |
| クエリパラメータ | /orders?version=1 |
URLは変わらないが、キャッシュの扱いに注意 |
| ヘッダー | Accept: application/vnd.example.v1+json |
URLがきれいだが、確認や試験がしにくい |
項目の追加のような、既存の利用者に影響しない変更では、バージョンを上げる必要はありません。項目の削除・名前の変更・型の変更のように、既存の利用者が動かなくなる変更をするときに、新しいバージョンを用意します。
冪等性とIdempotency-Key
通信が途中で切れた場合、利用者は「注文が作られたかどうか」がわかりません。POSTを再送すると、注文が二重にできるおそれがあります。
そこで、リクエストごとに一意のキー(Idempotency-Key)をヘッダーに付けて送り、サーバー側で同じキーのリクエストは1回だけ処理する方法があります。決済のAPIなどで広く使われている方法です。
先ほどの例で、同じキーのリクエストをもう一度送った結果です。
curl -X POST http://127.0.0.1:8765/v1/orders \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7c1e-01' \
-d '{"item":"マウス","quantity":2}'
{"id": 2, "item": "マウス", "quantity": 2, "status": "pending"}
新しい注文は作られず、1回目と同じ注文(id: 2)が返りました。再送しても安全なため、利用者は通信エラーのときに安心して再試行できます。実際の運用では、キーを一定期間(24時間など)保存し、同じキーで内容が違うリクエストが来た場合はエラーにします。
認証の渡し方
| 方法 | 例 | 用途 |
|---|---|---|
| Bearerトークン | Authorization: Bearer eyJhbGci... |
ログインした利用者の操作(OAuth・JWTなど) |
| APIキー | X-API-Key: sk_live_... |
システム同士の連携 |
- 認証の情報をURLに入れない(
?token=...はアクセスログやブラウザの履歴に残る) - 必ずHTTPSを使う
- 認証されていなければ401、権限がなければ403を返す
設計レビューのチェックリスト
| 観点 | チェック項目 |
|---|---|
| URL | 名詞・複数形になっているか。動詞が入っていないか |
| URL | 階層が深すぎないか(2階層程度まで) |
| メソッド | 取得はGET、作成はPOST、更新はPUT・PATCH、削除はDELETEになっているか |
| ステータス | エラーなのに200を返していないか |
| ステータス | 作成は201と Location、削除は204になっているか |
| エラー | 形式がAPI全体で統一されているか(RFC 9457など) |
| エラー | 入力エラーで、どの項目がなぜエラーなのかがわかるか |
| エラー | 内部の情報(SQL文、スタックトレース)を返していないか |
| 一覧 | ページングがあり、limit に上限があるか |
| 命名 | 項目名の書き方(created_at か createdAt か)が統一されているか |
| 日時 | ISO 8601形式(2026-09-28T10:00:00+09:00)で、タイムゾーンが明確か |
| 冪等性 | 二重に実行されると困るPOSTで、Idempotency-Keyなどの対策があるか |
| 認証 | 認証の情報をURLに入れていないか。401と403を使い分けているか |
| 互換性 | 既存の利用者が動かなくなる変更がないか。ある場合はバージョンを上げたか |
| 文書 | OpenAPIなどで仕様が文書化されているか |
よくある質問
Q. PUTとPATCHはどう使い分ければいいですか? A. PUTはリソース全体を送って置き換える、PATCHは変更したい項目だけを送る操作です。画面の一部の項目だけを更新することが多い場合は、PATCHの方が使いやすくなります。
Q. 項目名はスネークケースとキャメルケースのどちらがいいですか? A. どちらでも構いませんが、API全体で統一することが大切です。JavaScriptの画面から使うことが多いならキャメルケース、PythonやRubyのサーバーならスネークケースを選ぶことが多いです。
Q. RESTとGraphQLはどちらを選べばいいですか? A. 画面ごとに必要な項目が大きく異なり、1回のリクエストで複数のリソースをまとめて取得したい場合はGraphQLが向いています。外部に公開するAPIや、シンプルな業務システムでは、HTTPの仕組み(キャッシュ・ステータスコード)をそのまま活かせるRESTの方が扱いやすいことが多いです。
Q. エラーメッセージは日本語でもいいですか?
A. 画面にそのまま表示するメッセージであれば、利用者の言語で問題ありません。プログラムで判定するための type や、項目名などは英語の固定値にしておくと、表示の言語を変えても処理に影響しません。
まとめ
- URLは名詞・複数形、操作はHTTPメソッドで表す
- ステータスコードで結果を伝え、エラーなのに200を返さない
- エラーは RFC 9457 の形式に揃え、入力エラーは項目ごとにまとめて返す
- 一覧はページングし、
limitに上限を設ける。大量のデータにはカーソル方式 - 既存の利用者が動かなくなる変更をするときに、バージョンを上げる
- 二重に実行されると困るPOSTには、Idempotency-Keyで再送に備える
- 設計はチェックリストでレビューし、OpenAPIで文書化する
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「REST APIの設計|URL・ステータスコード・エラー形式の決め方」
株式会社RJCでは、使う人にとってわかりやすいAPIを、チームで議論しながら設計しています。こうした知識を仲間と共有しながら、一緒に成長を楽しんでいます。
株式会社RJC ― SI事業・SES事業・AI駆動開発。未経験からエンジニアに育てるのが得意な会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。