Node.js fetchのエラーハンドリング入門:TypeScriptで型安全に書く

スポンサーリンク

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); // ここには入らない
}

fetchthrow するのはネットワーク自体に問題があるときだけです。


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で型安全にする:カスタムエラークラス

statusbody を持つカスタムエラーを作ると、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) で実装できる

関連記事:Node.jsのfetchが動かないときの対処法