「エラーレスポンス処理」を一言でいうと
fetch / API 通信における「エラーレスポンス処理」は、
「サーバーから“うまくいかなかった”という返事が返ってきたときに、何をどうするか決めておくこと」 です。
非同期通信は「成功したとき」だけでなく、
「パラメータがおかしい」「ログインしてない」「サーバー側でバグった」など、さまざまな失敗パターンがあります。
それを全部「よく分からないエラー」で済ませるのか、
「入力ミス」「認証エラー」「サーバートラブル」のようにちゃんと分けて扱うのかで、
コードの質も、使う人の体験も、かなり変わります。
ここが重要です。
fetch は「通信エラー」と「HTTP エラー」を自動では区別してくれません。
だからこそ、自分で “エラーを受け止めて意味づけする処理” を書くことが、fetch を使いこなすカギ になります。
まず押さえるべき「3種類のエラー」
1. 通信そのものの失敗(ネットワークエラー)
これは「サーバーに届きもしなかった」ケースです。
例えば、次のような状況です。
ブラウザがオフラインになっている。
DNS 解決ができない(ドメインが存在しない)。
CORS 制限などでブラウザがリクエストをブロックした。
この場合、fetch 自体が reject(例外)されます。
async function fetchData() {
try {
const response = await fetch("https://example.com/api/data");
// 通信が成功すればここに来る
} catch (error) {
// 通信そのものが失敗したときはここに来る
console.error("ネットワークエラー:", error);
}
}
JavaScriptcatch に来た時点で、「サーバーのステータスコードを見て…」という話以前に、
そもそも相手に届いていない、というイメージです。
2. HTTP レベルのエラー(404, 500 など)
リクエストはサーバーに届いていて、サーバーもレスポンスを返しています。
ただし、それが「成功」ではなく「エラー」だというパターンです。
404 Not Found
500 Internal Server Error
401 Unauthorized(認証エラー)
403 Forbidden(権限なし)
などが代表例です。
このとき fetch 自体は成功扱いで、普通に response が返ってきます。
const response = await fetch("https://example.com/api/data");
// 404 や 500 でもここは通る
JavaScriptなので、response.ok や response.status を自分で確認する必要があります。
3. アプリケーションレベルのエラー(業務ロジックエラー)
HTTP 的には 200 OK だけど、
アプリとしては「うまくいっていない」ケースです。
例えば、
HTTP ステータスは 200
JSON の中身は { success: false, message: "入力値が不正です" }
というようなパターンです。
const data = await response.json();
if (!data.success) {
console.error("アプリケーションエラー:", data.message);
return;
}
JavaScriptここが重要です。
「エラー」と一言でいっても、
通信レベル、HTTP レベル、アプリケーションレベルの 3 層があります。
どの層で失敗しているのかを自分で見極め、その層に応じた処理を書く のが「エラーレスポンス処理」です。
fetch を使ったエラー処理の基本パターン(async / await)
一番ベーシックな「ちゃんとした書き方」
async function fetchUsers() {
try {
const response = await fetch("https://example.com/api/users");
if (!response.ok) {
throw new Error("HTTPエラー: " + response.status);
}
const data = await response.json();
if (data.error) {
throw new Error("APIエラー: " + data.error.message);
}
console.log("ユーザー一覧:", data);
} catch (error) {
console.error("何らかのエラーが発生:", error.message);
}
}
JavaScript流れを丁寧に追います。
まず await fetch(...) が成功するかどうか。
ここで失敗したら catch に飛ぶので、「ネットワーク系のエラー」として扱えます。
次に if (!response.ok) で HTTP ステータスを判定。
404, 500 など、200〜299 以外なら throw して catch に飛ばします。
さらに JSON を取り出したあと、data.error などアプリ側の失敗を自分でチェックし、
それも throw します。
こうすることで、
通信エラーも、HTTP エラーも、業務エラーも、
最終的にはすべて catch で拾えます。
ここが重要です。
エラーレスポンス処理のコア発想は、
「エラーをバラバラな場所で処理しないで、“意味のある Error オブジェクト” に変換して一箇所で受け止める」 ことです。
そのために、response.ok や data.error を見て、自分から throw するのがポイントです。
ステータスコード別に「意味のあるエラー」に変換する
生の数字ではなく、意味をつける
404, 401, 500…と生の数字だけ見ても、
後から読む自分にはあまり優しくありません。
そこで、「ステータスによってメッセージを変える」例を見てみます。
async function fetchProfile() {
try {
const response = await fetch("https://example.com/api/profile");
if (!response.ok) {
if (response.status === 401) {
throw new Error("ログインが必要です (401)");
}
if (response.status === 404) {
throw new Error("プロフィールが見つかりません (404)");
}
if (response.status >= 500) {
throw new Error("サーバー側で問題が発生しています (" + response.status + ")");
}
throw new Error("想定外のHTTPエラー: " + response.status);
}
const data = await response.json();
console.log("プロフィール:", data);
} catch (error) {
console.error(error.message);
}
}
JavaScriptここでやっていることは、
HTTP の生のステータスコード
→ 「ログインしてない」「リソースがない」「サーバーがおかしい」などの人間が理解できるメッセージ
に変換することです。
ここが重要です。
エラーレスポンス処理では、
「数字 → 意味」の翻訳をどこかで必ずやる必要があります。
1 カ所でこの翻訳をしてしまえば、そのあとは error.message をユーザーに見せるだけで済みます。
エラー時にもレスポンスボディ(JSON)を読みたい場合
API が「エラー詳細」を JSON で返すパターン
少しリッチな API だと、
エラー時にも JSON で詳しい情報を返してくれることがあります。
例えば、ステータス 400 でこんな JSON を返す API を想像します。
{
"errorCode": "VALIDATION_ERROR",
"message": "メールアドレスの形式が不正です"
}
JavaScriptこれを使うには、
HTTP エラーと分かっていても、あえて JSON を読む必要があります。
async function submitForm(payload) {
try {
const response = await fetch("https://example.com/api/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const contentType = response.headers.get("Content-Type") || "";
if (!response.ok) {
// JSON だったら詳細エラーを読む
if (contentType.includes("application/json")) {
const errorBody = await response.json();
throw new Error(errorBody.message || "不明なエラーが発生しました");
} else {
// JSON でない場合はテキストとして読む
const text = await response.text();
throw new Error(text || "不明なエラーが発生しました");
}
}
// 成功時の JSON
const data = await response.json();
console.log("成功:", data);
} catch (error) {
console.error("送信エラー:", error.message);
}
}
JavaScriptポイントは、
「!response.ok だけど response.json() したいこともある」ということ。
その場合、Content-Type を見たり、API の仕様書を確認して、
「エラー時も JSON なのか、テキストなのか」を把握しておくと安全です。
ここが重要です。
エラー時のレスポンス形式は API ごとに違います。
「ステータスがエラーだから JSON は読まない」ではなく、
仕様書を読んで「エラー時も JSON を返す API」なら、
意図的に response.json() して詳細を取りに行く という判断が必要です。
then / catch で書くエラーレスポンス処理(Promise チェーン)
async / await の裏側をそのまま見た形
同じことを then / catch で書くとこうなります。
fetch("https://example.com/api/users")
.then((response) => {
if (!response.ok) {
return response.json().then((errorBody) => {
throw new Error(errorBody.message || "HTTPエラー: " + response.status);
});
}
return response.json();
})
.then((data) => {
console.log("ユーザー一覧:", data);
})
.catch((error) => {
console.error("エラー:", error.message);
});
JavaScript最初の then で response を受け取り、!response.ok の場合は response.json() でエラー詳細を読んでから throw しています。
成功パスでは response.json() を return。
その結果が次の then の data に渡されます。
ここが重要です。
書き味は違って見えても、
やっていることは「ok を見て判断し、必要なら JSON を読んで Error に変換する → catch でまとめて扱う」だけ です。
async / await でしっかり理解してから、then / catch の形も読めるようになると、既存コードも怖くありません。
初心者として「エラーレスポンス処理」で本当に押さえてほしいこと
fetch のエラーは大きく分けて
「通信エラー(catch に来る)」「HTTP エラー(status / ok を見る)」「アプリケーションエラー(JSON の中身を見る)」の 3 段階がある。
fetch 自体は 404 や 500 でもエラーを投げないので、response.ok や response.status を自分でチェックし、
必要なら throw new Error(...) で例外に変換する。
エラー情報を JSON で返してくれる API では、
エラー時にも response.json() して message などを読み取り、
人間に分かりやすいエラーメッセージに変換してから扱う。
最終的には、意味のある Error オブジェクトに変換して、try { ... } catch (error) { ... } や .catch(error => { ... }) の中で一括処理する構造にすると、
コードの見通しが良くなり、エラーハンドリングを変更しやすくなる。
ここが重要です。
エラーレスポンス処理は、「失敗をただの失敗として捨てる」のではなく、
“なぜ失敗したのか” を浮かび上がらせてあげる仕事です。response.status や response.json() を見るときは、
「このエラーを見たときに、未来の自分やユーザーが次の一手を決められるか?」を意識してみてください。
そう考えてエラー処理を書くと、fetch / API 通信はただ「動くかどうか」から一段進んで、
“ちゃんと会話しているコード” に変わっていきます。
