Node.js fetchのエラーハンドリング入門:TypeScriptで型安全に書く
Node.js 18以降、fetch がネイティブで使えます。
ただし fetch のエラーハンドリングはAxiosと挙動が異なり、知らないとハマります。
この記事では2種類のエラーの違いから、TypeScriptで型安全に扱う方法までまとめます。
fetchのエラーはAxiosと違う
Axiosは4xx・5xxのHTTPエラーでも catch に入ります。
fetchは入りません。
// ❌ よくある間違い:404でもcatchに入ると思っている
try {
const res = await fetch('https://api.example.com/users/999');
const data = await res.json(); // 404のままここが実行される
} catch (e) {
console.error(e); // ここには入らない
}
fetch が throw するのはネットワーク自体に問題があるときだけです。
2種類のエラーを理解する
| エラー種別 | 発生条件 | throwするか |
|---|---|---|
| ネットワークエラー | DNS解決失敗・接続拒否・オフライン | する(TypeError) |
| HTTPエラー | 4xx / 5xx レスポンス | しない |
| タイムアウト | AbortSignal でキャンセル | する(DOMException) |
基本パターン:response.ok でHTTPエラーを検出する
response.ok は status が 200〜299 のとき true になります。
const res = await fetch('https://api.example.com/users/1');
if (!res.ok) {
throw new Error(`HTTP error: ${res.status}`);
}
const data = await res.json();
これだけでHTTPエラーを検出できます。
TypeScriptで型安全にする:カスタムエラークラス
status や body を持つカスタムエラーを作ると、catch 内で種別を判別できます。
class HttpError extends Error {
constructor(
public readonly status: number,
public readonly body: unknown,
message?: string,
) {
super(message ?? `HTTP ${status}`);
this.name = 'HttpError';
}
}
async function fetchJson<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) {
const body = await res.json().catch(() => null);
throw new HttpError(res.status, body);
}
return res.json() as Promise<T>;
}
try {
const user = await fetchJson<{ name: string }>('https://api.example.com/users/1');
console.log(user.name);
} catch (e) {
if (e instanceof HttpError) {
console.error(`HTTPエラー ${e.status}:`, e.body);
} else if (e instanceof TypeError) {
console.error('ネットワークエラー:', e.message);
} else {
throw e;
}
}
instanceof で3種類を型安全に振り分けられます。
タイムアウトの実装:AbortSignal.timeout
AbortSignal.timeout(ms) で指定ミリ秒後にキャンセルできます(Node.js 18+)。
try {
const res = await fetch('https://api.example.com/data', {
signal: AbortSignal.timeout(3000), // 3秒でタイムアウト
});
const data = await res.json();
} catch (e) {
if (e instanceof DOMException && e.name === 'TimeoutError') {
console.error('タイムアウト');
} else {
throw e;
}
}
まとめ:エラー種別と対処法
| エラー種別 | 検出方法 | 型 | 対処 |
|---|---|---|---|
| ネットワークエラー | catch に入る |
TypeError |
再試行・フォールバック |
| HTTPエラー | !response.ok |
カスタム HttpError |
ステータスに応じた処理 |
| タイムアウト | catch に入る |
DOMException |
タイムアウト時間の調整 |
fetchは HTTPエラーで throw しない —response.okのチェックを忘れない- カスタムエラークラスを作ると
instanceofで型安全に振り分けられる - タイムアウトは
AbortSignal.timeout(ms)で実装できる