APIを実装していると、一度はこれで悩むと思う。
データが存在しないときって404を返すべきなのか、それとも空配列を返すべきなのか。
自分も実務でここが曖昧なまま実装して、後からフロント側で扱いにくくなったことがあったので、一度整理してみた。
結論
- リソース自体が存在しない → 404
- リソースは存在するが中身が空 → 200 + 空配列
ただし、これはあくまで一般的なパターンであって絶対ではない。
一番大事なのは「意味に一貫性があるかどうか」。
具体例で考える
1. 404を返すケース
GET /users/123
このとき、user自体が存在しない場合。
404 Not Found
これは分かりやすくて、URLで指定しているリソースそのものが存在しない。
つまり
「このリソースはない」という意味。
2. 空配列を返すケース
GET /users/123/posts
このとき、userは存在するけど投稿が0件の場合。
200 OK
[]
これは
「ユーザーは存在しているけど、投稿はまだない」
という意味になる。
よくあるズレ
ここがチームでズレると一気に面倒になる。
全部404にするパターン
投稿が0件でも404にするケース。
これをやると
- 「投稿が0件」なのか
- 「ユーザーが存在しない」のか
区別がつかなくなる。
フロント側も分岐が書きにくくなる。
全部200にするパターン
全部200で返して、存在しない場合はnullや空配列で表現するケース。
これもやりがちだけど
- 本当に存在しないのか
- ただ空なのか
が曖昧になる。
結果的にバグに気づきにくくなる。
本質
この問題で一番大事なのはこれ。
「そのレスポンスが何を意味しているかが明確か」
例えば
[]
これは「存在しているけど中身がない」
404
これは「そもそも存在しない」
この意味がAPI全体でブレていなければ、設計としては成立する。
設計としてどう考えるか
よくある整理としてはこう。
-
単体リソース
/users/123→ なければ404
-
コレクション
/users/123/posts→ なければ空配列
このルールにしておくと、フロント側もかなり扱いやすくなる。
実務で一番大事なこと
ここが結構重要。
「どっちを選ぶか」よりも
- ルールが統一されているか
- 仕様として明文化されているか
のほうが大事。
特にフロントエンドに対しては、
・単体取得で存在しない場合は404を返すのか
・一覧取得で0件の場合は空配列を返すのか
・それとも存在しない場合でも200でnullを返すのか
といった「ケースごとの挙動」を仕様として明確にしておく必要がある。
例えば
- ユーザー詳細APIで存在しない場合 → 404
- ユーザーの投稿一覧で0件の場合 → []
- 検索APIでヒットしない場合 → []
のように、「どういう状況のときにどう返すか」を決めておく。
これを決めずに実装してしまうと、APIごとにレスポンスの意味がバラバラになってしまい、フロント側で毎回個別にハンドリングが必要になってバグの温床になる。
まとめ
重要なのは404か空配列かではなく、
「このAPIはどういう意味を持ってそのレスポンスを返しているのか」
を設計として決めること。
そしてそのルールを仕様としてフロントエンドにちゃんと伝えること。
ここができていれば設計として破綻しないし、逆にここが曖昧だと後から確実に困る。