JavaScript fetchが動作しない原因と解決方法|初心者向け完全ガイド

JavaScript

JavaScript fetchが動作しない原因と解決方法|初心者向け完全ガイド

JavaScriptのfetch関数は、サーバーからデータを取得する際に非常に便利な機能です。しかし、初心者がfetchを使用する際に「fetchがうまく動作しない」「エラーが出ている」というトラブルに直面することは珍しくありません。本記事では、fetchが動作しない主な原因と、その解決方法について詳しく解説します。

JavaScriptのfetchが動作しない主な原因

fetchが正常に動作しないケースにはいくつかの原因が考えられます。ここでは、最も一般的な原因を5つご紹介します。

1. CORSエラー(Cross-Origin Resource Sharing)

最も多く発生するのがCORSエラーです。異なるドメイン間でfetchを実行しようとする際に、ブラウザのセキュリティ機能によってリクエストがブロックされます。例えば、example.comからapi.other-domain.comへのリクエストはCORSポリシーに違反する可能性があります。

CORSエラーが発生する具体的な状況:

  • 異なるドメインへのリクエスト
  • 異なるプロトコル(httpとhttps)間でのリクエスト
  • 異なるポート番号へのリクエスト
  • サーバー側で適切なCORSヘッダーが設定されていない

2. ネットワークエラーまたは接続の問題

インターネット接続が不安定だったり、サーバーが停止していたりする場合、fetchは失敗します。また、タイムアウトが発生することもあります。

3. URLの指定が間違っている

単純ですが非常に多い原因です。URLのタイプミスや存在しないエンドポイントへのリクエストは必ず失敗します。

4. HTTPメソッドが適切でない

GETリクエストで送信すべき場面でPOSTを使用したり、サーバーが対応していないメソッドを使用したりすると、エラーが発生します。

5. レスポンスデータの形式が正しくない

JSONとして解析できないデータをresponse.json()で処理しようとするとエラーになります。

fetchが動作しない場合の解決手順

ステップ1:ブラウザの開発者ツールを確認する

まず最初にすべき作業は、ブラウザの開発者ツールでエラーメッセージを確認することです。

  1. F12キーを押すか、右クリック → 「検査」を選択
  2. 「Console」タブを開く
  3. 赤色のエラーメッセージを確認
  4. 「Network」タブでリクエストの状態を確認

エラーメッセージに「CORS」と表示されていれば、CORSエラーが原因です。

ステップ2:URLと接続性を確認する

次に、指定しているURLが正確かどうか、サーバーが稼働しているかを確認します。

  • URLをブラウザのアドレスバーに直接入力して、ページが表示されるか確認
  • スペルミスや大文字・小文字の違いをチェック
  • ポート番号が正しいか確認

ステップ3:HTTPメソッドとリクエストボディを確認する

APIドキュメントを確認して、適切なHTTPメソッド(GET、POST、PUT、DELETEなど)を使用しているか確認しましょう。

ステップ4:レスポンスの形式を確認する

Network タブで実際のレスポンスを確認し、それが正しい形式であるかを検証します。

JavaScriptのfetch – 解決方法とコード例

基本的なfetchの使い方

まず、fetchの基本的な構文を確認しましょう。

// シンプルなGETリクエスト
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('エラー:', error));

CORSエラーの解決方法

パターン1:サーバー側で対応する(推奨)

サーバー側で適切なCORSヘッダーを設定します。

// Node.js + Expressの例
const express = require('express');
const cors = require('cors');
const app = express();

// CORSミドルウェアを使用
app.use(cors({
  origin: 'https://example.com', // 許可するオリジン
  credentials: true
}));

app.get('/api/data', (req, res) => {
  res.json({ message: 'データ取得成功' });
});

パターン2:クライアント側でfetchオプションを調整する

fetch('https://api.example.com/data', {
  method: 'GET',
  mode: 'cors', // CORS対応
  credentials: 'include', // クッキーを含める
  headers: {
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('エラー:', error));

POSTリクエストの例

データを送信する場合はPOSTメソッドを使用します。

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: '田中太郎',
    email: 'tanaka@example.com'
  })
})
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTPエラー! ステータス: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log('成功:', data))
  .catch(error => console.error('エラー:', error));

エラーハンドリングの完全版

本番環境では、適切なエラーハンドリングが必須です。

async function fetchWithErrorHandling(url, options = {}) {
  try {
    const response = await fetch(url, options);
    
    // ステータスコードをチェック
    if (!response.ok) {
      throw new Error(`HTTPエラー! ステータス: ${response.status}`);
    }
    
    // Content-Typeをチェック
    const contentType = response.headers.get('content-type');
    if (!contentType || !contentType.includes('application/json')) {
      throw new Error('レスポンスがJSON形式ではありません');
    }
    
    const data = await response.json();
    return data;
    
  } catch (error) {
    if (error instanceof TypeError) {
      console.error('ネットワークエラー:', error.message);
    } else if (error instanceof SyntaxError) {
      console.error('JSONパースエラー:', error.message);
    } else {
      console.error('エラー:', error.message);
    }
    throw error;
  }
}

// 使用例
fetchWithErrorHandling('https://api.example.com/data')
  .then(data => console.log('取得データ:', data))
  .catch(error => console.error('処理失敗'));

タイムアウト機能の実装

fetchにはデフォルトでタイムアウト機能がないため、手動で実装する必要があります。

function fetchWithTimeout(url, timeout = 5000, options = {}) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  return fetch(url, {
    ...options,
    signal: controller.signal
  })
    .then(response => {
      clearTimeout(timeoutId);
      return response;
    })
    .catch(error => {
      clearTimeout(timeoutId);
      if (error.name === 'AbortError') {
        throw new Error(`リクエストがタイムアウト(${timeout}ms以上)`);
      }
      throw error;
    });
}

// 使用例:5秒のタイムアウトを設定
fetchWithTimeout('https://api.example.com/data', 5000)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('エラー:', error.message));

JavaScriptのfetch – よくある間違いと対策

間違い1:エラーをチェックしていない

❌ 間違い:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data));
  // エラーハンドリングがない!

✅ 正解:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTPエラー! ステータス: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('エラー:', error));

間違い2:POSTでbodyをJSON文字列化していない

❌ 間違い:

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: { name: '田中太郎' } // オブジェクトのまま!
});

✅ 正解:

fetch('https://api.example.com/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: '田中太郎' }) // JSON文字列化
});

間違い3:テキストレスポンスをJSONで解析しようとする

❌ 間違い:

fetch('https://api.example.com/data')
  .then(response => response.json()) // テキストなのにJSON解析!
  .catch(error => console.error(error));

✅ 正解:

fetch('https://api.example.com/data')
  .then(response => {
    const contentType = response.headers.get('content-type');
    if (contentType && contentType.includes('application/json')) {
      return response.json();
    } else {
      return response.text();
    }
  })
  .catch(error => console.error(error));

間違い4:async/awaitでエラーハンドリングを忘れている

❌ 間違い:

async function getData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  return data;
  // try-catchがない!
}

✅ 正解:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTPエラー! ステータス: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('データ取得失敗:', error);
    throw error;
  }
}

間違い5:同一オリジンでもプロトコルやポートが異なる場合を見落とす

http://localhost:3000http://localhost:8000は異なるオリジンです。異なるオリジン間のリクエストはCORSの影響を受けます。

デバッグのためのテクニック

Network タブを活用する

開発者ツールのNetworkタブで以下を確認しましょう:

  • リクエストが実際に送信されているか
  • ステータスコードが何か(200、404、500など)
  • レスポンスヘッダーに「Access-Control-Allow-Origin」があるか
  • 実際のレスポンスボディは何か

ログを活用したデバッグ

fetch('https://api.example.com/data')
  .then(response => {
    console.log('ステータス:', response.status);
    console.log('ヘッダー:', response.headers);
    return response.json();
  })
  .then(data => {
    console.log('レスポンスボディ:', data);
  })
  .catch(error => {
    console.error('エラータイプ:', error.name);
    console.error('エラーメッセージ:', error.message);
  });

まとめ

JavaScriptのfetchが動作しない原因は複数ありますが、以下のポイントを押さえることで、ほとんどの問題は解決できます:

  • CORSエラーが最多:異なるオリジン間でのリクエストはサーバー側でCORS対応が必要
  • エラーハンドリングは必須response.okをチェックし、.catch()でエラーを処理
  • 開発者ツールを活用:Network タブとConsoleでエラーの原因を特定
  • データ形式の確認:JSON、テキスト、BlobなどのContent-Typeに合わせた処理
  • タイムアウト実装:本番環境ではタイムアウト機能の実装が推奨される

これらのポイントを意識することで、fetchを安全かつ確実に使用できるようになります。初心者の方は、まず基本的なエラーハンドリングから始めることをお勧めします。不明点がある場合は、ブラウザの開発者ツールで実際のリクエストとレスポンスを確認し、段階的に問題を解決していくとよいでしょう。

タイトルとURLをコピーしました