まず結論
fetch は、404や500が返っても、それだけでは例外にならないことがあります。
通信としてレスポンスを受け取れた場合、fetch 自体は成功扱いになります。そのため、response.ok や response.status を見て判断します。
よくある勘違い
次のコードは、404でも catch に入るとは限りません。
try {
const response = await fetch("/api/posts");
const data = await response.json();
} catch (error) {
console.error("失敗", error);
}
サーバが404を返していても、HTTPレスポンスは返ってきています。ネットワークとしては完了しているため、fetch はresolveされます。
response.status
response.status にはHTTPステータスコードが入ります。
const response = await fetch("/api/posts");
console.log(response.status); // 200, 404, 500 など
statusを見れば、成功なのか、認証エラーなのか、サーバエラーなのかを分けられます。
response.ok
response.ok は、statusが200番台なら true になります。
const response = await fetch("/api/posts");
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
これにより、404や500を自分でエラーとして扱えます。
JSON解析エラー
APIがJSONを返す想定でも、実際にはHTMLが返っていることがあります。
例:
- 404ページのHTML
- ログインページのHTML
- サーバエラーページ
その状態で response.json() を呼ぶと、JSON解析エラーになります。
Unexpected token '<'
< はHTMLの先頭であることが多いです。
安全な書き方
const response = await fetch("/api/posts");
if (!response.ok) {
const text = await response.text();
throw new Error(`HTTP ${response.status}: ${text.slice(0, 120)}`);
}
const data = await response.json();
エラー時は、まずテキストで中身を見ると調査しやすいです。
ただし、本番で詳細なエラーをユーザーにそのまま表示するのは避けます。ログや開発時の確認に使います。
切り分け
| 状態 | 見ること |
|---|---|
| fetchがcatchに入る | ネットワーク断、CORS、URL不正など |
| response.okがfalse | HTTPステータスが200番台ではない |
| json()で落ちる | JSONではないものが返っている |
| statusは200だが画面が変 | データ構造や画面側処理 |
実践メモ: API通信では「fetchが成功したか」と「HTTPとして成功したか」を分けて考えます。
まとめ
fetch は、HTTPの404や500だけでは例外にならないことがあります。response.ok と response.status を見て、自分で成功・失敗を判断します。
JSON解析エラーが出た時は、APIが本当にJSONを返しているかをNetworkで確認します。