10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事の狙い

ガイド記事は丁寧ですが
実務では まず事故を避けたい というニーズが強いです
そこで本記事では

  • よくある事故パターン
  • 何が問題か
  • どう直すか
  • レビューで使えるチェックリスト

をセットでまとめます
読むだけでなく コピペしてチームのレビューテンプレにできます

想定読者

  • APIを作る側 バックエンド フロントの両方
  • 仕様が増えてAPIが崩れ始めたチーム
  • なんとなく動くが 運用でつらくなってきた人

先に結論

  • URLは名詞で表す 動詞を混ぜない
  • エラーはコードと機械可読な形式で返す
  • ページング ソート フィルタは規約で固める
  • 冪等性と再送を最初から考える
  • 互換性を壊す変更はバージョニングで制御する

アンチパターン集

動詞をURLに入れる

  • POST /users/create
  • POST /orders/cancel

何がまずいか

  • HTTPメソッドの意味と二重化する
  • 一貫性が崩れ 増えるほど命名が地獄になる

直し方

  • POST /users
  • POST /orders/{id}/cancellation

考え方

リソースを名詞で表し
操作はHTTPメソッドとサブリソースで表現します

ステータスコードが常に200

  • 失敗しても200でerrorを返す

何がまずいか

  • クライアントが成功か失敗かを正しく扱えない
  • 監視やリトライの仕組みが壊れる

直し方の目安

  • 400 入力不正
  • 401 未認証
  • 403 権限なし
  • 404 リソースなし
  • 409 競合
  • 422 ビジネスルール違反
  • 429 レート制限
  • 500 サーバー内部

補足

APIゲートウェイやSDKがステータスコード前提で作られていることが多いです
まずは正しいステータスを返すだけで運用が楽になります

エラーレスポンスがバラバラ

エンドポイント毎にエラー形式が違い
messageだけだったり fieldが無かったりする

何がまずいか

  • フロント実装が複雑化する
  • ログと監視で集計できない

直し方

最低限 次を統一します

  • code 機械可読なエラーコード
  • message 人間向けの簡潔な説明
  • details 入力エラーの配列など
  • traceId 問い合わせ用ID

{
"code": "VALIDATION_ERROR",
"message": "入力に誤りがあります",
"details": [
{"field": "email", "reason": "invalid_format"}
],
"traceId": "01J..."
}

ページングが設計されていない

症状

  • 一覧がlimitなし
  • 途中からoffsetが重くなる

何がまずいか

  • データが増えると突然遅くなる
  • DBとAPIのコストが雪だるま式に上がる

直し方

  • 小規模は limit offset
  • 大規模や更新頻度が高いなら cursor pagination

カーソル型の考え方

  • sortキーとして単調増加の値 例 createdAt id を使う
  • 次ページは after=lastSeenKey で指定

ソート フィルタの規約がない

  • sort=createdAt_desc
  • order=desc&sortBy=createdAt
  • filter=name:foo

何がまずいか

  • エンドポイント毎に異なると覚えられない
  • SDKや共通コンポーネントが作れない

直し方

次のように規約を決めると強いです

  • sort=createdAt
  • order=asc or desc
  • filter[field]=value 形式

GET /users?filter[status]=active&sort=createdAt&order=desc&limit=20

互換性が壊れる変更を平気で入れる

  • レスポンスのフィールド名を変更
  • フィールドの型を変更
  • 必須項目を追加

何がまずいか

  • クライアントが即死する
  • モバイルアプリの審査や配信の都合で復旧が遅れる

直し方

  • 追加は基本的に安全 既存フィールドを残す
  • 破壊的変更はバージョンを分ける

バージョニングの現実的な落とし所

  • URLに v1 v2 を切る
  • 重大変更の時だけ切る
  • 期限を決めて古い版を廃止する

冪等性を考えていない

症状

  • ネットワーク再送で二重注文
  • タイムアウト後リトライで二重課金

何がまずいか

  • 最悪の事故に直結する

直し方

  • Idempotency-Keyをサポートする
  • サーバー側でキー単位の処理結果を保存して再利用

POST /payments
Idempotency-Key: 9c7f...

注意

キーの保持期間や競合時の挙動も決めます

PUTとPATCHが曖昧

症状

  • PUTが部分更新になっている
  • PATCHが全更新になっている

何がまずいか

  • クライアントが意図せずデータを消す
  • 競合や差分更新が難しくなる

直し方の目安

  • PUTはリソース全体の置き換え
  • PATCHは部分更新

ただし 現実にはPUTを部分更新として統一するチームもあります
重要なのは どちらに寄せるかを決めてドキュメント化することです

日時とタイムゾーンが適当

症状

  • 文字列だがフォーマットが曖昧
  • ローカル時刻のまま返す

何がまずいか

  • 集計やソートが壊れる
  • 国際化で破綻する

直し方

  • ISO 8601でUTCを返す
  • 例 2025-12-25T10:30:00Z

大きすぎるレスポンス

症状

  • 一覧で詳細まで全部返す

何がまずいか

  • 体感が遅い
  • モバイル回線で厳しい
  • サーバーコストが上がる

直し方

  • 一覧は要約だけ
  • 詳細は個別取得
  • expandパラメータで段階的に増やす

GET /orders?expand=items

認可が後付け

症状

  • 認証はあるが認可がない
  • userIdをパラメータでもらって信用する

何がまずいか

  • 参照改ざん IDOR になる

直し方

  • サーバー側で 誰が呼んだか をコンテキストとして持つ
  • クライアントのuserId指定に依存しない

レート制限がない

症状

  • 無制限に叩ける

何がまずいか

  • 悪用やバグで落ちる
  • リソースが枯渇する

直し方

  • 429を返す
  • クライアントにはRetry-Afterで待ち時間を返す

レビュー用チェックリスト

設計レビューやPRレビューで使えます

  • URLが名詞になっている
  • ステータスコードが妥当
  • エラーフォーマットが統一されている
  • ページングがある 一覧はlimit必須
  • ソート フィルタ規約が守られている
  • 破壊的変更の扱いが明確
  • 冪等性が必要な操作はIdempotency-Keyを持つ
  • 日時はUTC ISO 8601
  • 一覧レスポンスが過剰でない
  • 認可がサーバー側で完結している
  • レート制限と429がある
  • ログにtraceIdが残る

まとめ

REST APIは 最初は動けば勝ち に見えます
しかし 事故るポイントは毎回似ています

アンチパターンを先に潰しておくと

  • 開発速度
  • 運用負荷
  • セキュリティ

の全部が楽になります

本記事のチェックリストをそのままチームの規約のたたき台にしてみてください

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?