【JS応用|初心者向け】Fetch APIでハマりやすい罠!bodyUsedプロパティで「読み取り済みエラー」を防ごう

1. 導入:なぜbodyUsedを知る必要があるのか?

JavaScriptでWeb APIからデータを取得する際、必ずといっていいほどお世話になるのが「Fetch API」です。しかし、初心者の方がよく直面するのが「TypeError: Failed to execute ‘json’ on ‘Response’: body is already used」というエラーです。これは、一度読み取ったレスポンスを二度使おうとしたときに発生します。この「レスポンスがすでに使われたかどうか」を教えてくれるのが、今回解説するbodyUsedプロパティです。これを知ることで、データ取得時の不具合を未然に防ぐことができます。

2. 基礎知識:なぜデータは一度しか読めないのか?

Fetch APIで返されるレスポンスのボディ(本体)は、「ストリーム」という仕組みで提供されます。ストリームとは、データが少しずつ流れてくる川のようなものです。一度読み取って消費してしまうと、川の水が空になってしまうため、二度目の読み取りができません。
bodyUsedは、そのレスポンスがすでに「消費された(読み取られた)」かどうかをboolean型(true/false)で返してくれるプロパティです。

3. 実装/解決策:読み取り状況をチェックする

実装の基本は、「データを読み取る前にbodyUsedを確認する」または「取得したデータを変数に格納して再利用する」というアプローチです。
単純にAPIからデータを取得して表示するだけであれば問題ありませんが、デバッグ目的でログを出力したいときなどに、うっかり二回読み取りを行ってしまうミスが多発します。

4. サンプルプログラム

以下は、安全にレスポンスを処理するためのサンプルコードです。

async function fetchData() {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');

  // bodyUsedの初期値はfalseです
  console.log('読み取り前:', response.bodyUsed); // false

  // データを読み取ります
  const data = await response.json();
  console.log('データ取得完了:', data);

  // 読み取った後はbodyUsedがtrueになります
  console.log('読み取り後:', response.bodyUsed); // true

  // ここで再度読み取ろうとするとエラーになります
  // if (!response.bodyUsed) {
  //   const data2 = await response.json();
  // } else {
  //   console.log('エラー回避: すでに読み取られています');
  // }
}

fetchData();

5. 応用・注意点:現場でのベストプラクティス

現場の開発では、以下のようなポイントに注意してください。

・データを使い回す場合は変数に代入する
一度json()やtext()を実行して取得したデータは、メモリ上の変数(例: const data)に保持されます。レスポンスオブジェクト(response)を何度も触るのではなく、取得した変数を利用するようにしましょう。

・レスポンスを複製する(clone)
どうしてもレスポンスオブジェクトそのものを二度読み取りたい場合は、response.clone()メソッドを使用します。これにより、元のレスポンスを消費せずにコピーを作成して処理することが可能です。ただし、メモリを消費するため、必要なときだけ使用するようにしましょう。

・デバッグ時の注意
console.log(response) を実行する際、ブラウザのコンソールでレスポンスを展開すると、内部的に読み取りが発生してしまい、その後のプログラムでエラーになるケースがあります。開発中は「bodyUsedの状態」を意識してコードを書く癖をつけましょう。

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