基礎から学ぶJavaScript入門 90日コース | 非同期処理とAPI - Day 63:APIエラー処理

JavaScript 90日で身につけるJavaScript
スポンサーリンク
スポンサーリンク

Day 63:APIエラー処理

Day 63では、第5フェーズ「非同期処理とAPI」の実践として、APIエラー処理を学びます。今回の学習内容は、通信エラー、try / catch、エラーメッセージの3つです。Day 59からDay 62までで、fetch() を使ったAPI通信、公開APIからのデータ取得、取得したデータの画面表示、ローディング処理までを学んできました。ここまでで、成功したときの流れはかなり見えてきたはずです。しかし、実際のアプリ開発では「うまくいく場合」だけを考えていては不十分です。むしろ大切なのは、「うまくいかなかったときにどうするか」を設計することです。

API通信は、自分のプログラムの外にある相手とやり取りします。そのため、失敗する要因がたくさんあります。ネットワーク接続が切れているかもしれませんし、サーバーが一時的に止まっているかもしれません。URLを間違えることもありますし、サーバーから返ってきた内容が想定外であることもあります。こうした失敗は、特別な異常事態ではなく、実際には普通に起こりうるものです。だからこそ、エラー処理は「追加機能」ではなく、APIを使うなら最初から必須の設計だと考える必要があります。

初心者の方は、どうしても「成功したときに動くコード」を先に書きたくなりますし、それ自体は学習の入り口として大切です。しかし、ここから一歩進んで、「失敗してもユーザーが混乱しない」「プログラムが途中で壊れたように見えない」ようにすることが、実用的なアプリづくりには欠かせません。Day 63では、通信エラーとは何か、try / catch をどのように使うのか、そして画面へどんなエラーメッセージを出すべきかを、初心者向けにかみ砕いて詳しく説明していきます。特に大事なところは、「どの失敗をコードで受け止めるのか」「ユーザー向けメッセージと開発者向け確認をどう分けるか」です。ここをしっかり理解すると、APIを使うアプリの完成度が一段上がります。

まず最初に結論をつかむ

最初に今回の内容を一言でまとめると、APIエラー処理とは「通信やデータ取得が失敗したときに、その失敗をコードで受け止め、ユーザーにわかりやすく伝える仕組み」です。

流れをシンプルにすると、次のようになります。

  • APIへリクエストを送る
  • 成功したらデータを表示する
  • 失敗したら `catch` で受け止める
  • エラーメッセージを画面に表示する
  • ローディング中なら最後に確実に解除する

初心者の方は、まず「失敗を無視しない」「失敗したら伝える」という2点を中心に考えると理解しやすいです。

通信エラーとは何か

通信エラーとは、APIとのやり取りがうまく完了しない状態のことです。もっとやさしく言えば、「外部のデータを取りに行こうとしたけれど、何らかの理由でうまく受け取れなかった状態」です。

初心者の方は、通信エラーという言葉を聞くと難しく感じるかもしれませんが、原因としては身近なものが多いです。たとえば次のようなものがあります。

  • インターネット接続が切れている
  • APIのURLが間違っている
  • サーバーが一時的に停止している
  • サーバーがエラーを返している
  • 想定と違うデータ形式が返ってくる

つまり、通信エラーは珍しいものではなく、いつでも起こりうるものです。初心者の方は、まず「API通信は失敗することが普通にある」と思っておくとよいです。

なぜエラー処理が必要なのか

ここはDay 63で特に重要な部分です。なぜエラー処理が必要なのかというと、失敗したときに何も対策がないと、ユーザーにも開発者にも不便だからです。

たとえば、エラー処理がないと次のような問題が起きやすいです。

  • 画面が何も変わらず、止まったように見える
  • ローディング表示が消えずに残る
  • 何が原因かわからず、修正しにくい
  • ユーザーが何度もボタンを押してしまう

エラー処理があると、少なくとも「失敗したこと」はわかりますし、ユーザーへも「今はうまくいかなかった」と伝えられます。初心者の方は、エラー処理を「問題を完全に防ぐ仕組み」ではなく、「問題が起きたときに混乱を減らす仕組み」と考えると理解しやすいです。

try / catch とは何か

次に学ぶのが try / catch です。これは、処理の中でエラーが起きるかもしれない部分を囲って、もし失敗したら別の処理へ分けるための仕組みです。

書き方の基本形は次の通りです。

try {
  // うまくいくかもしれない処理
} catch (error) {
  // エラーが起きたときの処理
}
JavaScript

ここで、

  • `try` の中には、まず通常の処理を書きます。
  • `catch` の中には、失敗したときに行う処理を書きます。

初心者の方は、try を「まずやってみる場所」、catch を「失敗したときの受け止め場所」と考えるとイメージしやすいです。

async / await と try / catch の関係

Day 58で学んだ async / await と try / catch は非常に相性がよいです。await している非同期処理が失敗したとき、その失敗を catch 側で受け取れます。

たとえば、次のようなコードになります。

async function loadData() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
    const data = await response.json();

    console.log(data);
  } catch (error) {
    console.log("エラーが発生しました。", error);
  }
}
JavaScript

このコードでは、fetch() や response.json() のどこかでエラーが起きた場合に、catch の中へ移ります。初心者の方は、「await を使う非同期処理では、失敗したら catch に飛ぶ」と理解すると整理しやすいです。

まずは意図的に失敗させてみる

初心者の方に特におすすめなのは、わざと失敗するコードを試すことです。成功だけを見ていると、エラー処理のありがたさがわかりにくいからです。たとえば、URLをわざと間違えてみます。

async function loadData() {
  try {
    // わざと間違ったURLを指定する
    const response = await fetch("https://jsonplaceholder.typicode.com/invalid-url");
    const data = await response.json();

    console.log(data);
  } catch (error) {
    console.log("エラーが発生しました。", error);
  }
}

loadData();
JavaScript

このようにすると、失敗したときに catch がどのように動くかを確認できます。初心者の方は、「失敗を観察する練習」も大切だと考えると、理解がぐっと深まります。

ただし fetch() では注意点がある

ここは非常に重要なので深掘りします。初心者の方が fetch() のエラー処理でよく混乱する点があります。それは、「HTTPエラーが必ずしも自動で catch に入るわけではない」ということです。

たとえば、URLの書き方が完全に壊れていたり、ネットワーク自体がつながらなかったりすれば catch に入ります。しかし、サーバーが 404 や 500 のようなエラー応答を返した場合、fetch() 自体はレスポンスを受け取ったと考えるため、そのまま catch に入らないことがあります。

つまり、fetch() では次の2種類を分けて考える必要があります。

  • 通信自体に失敗した場合
  • 通信はできたが、返ってきた結果がエラーだった場合

初心者の方は、この違いを最初から完璧に覚えなくてもよいですが、「try / catch だけで全部のエラーが自動処理されるわけではない」と知っておくことはとても大切です。

response.ok を確認する

そこで役立つのが response.ok です。これは、レスポンスが成功とみなせるかどうかを表す値です。もし ok が false なら、自分でエラーとして扱うことができます。

async function loadData() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");

    // HTTPエラーなら自分でエラーにする
    if (!response.ok) {
      throw new Error("サーバーエラーが発生しました。");
    }

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.log("エラーが発生しました。", error);
  }
}

loadData();
JavaScript

このコードでは、response.ok が false のとき、自分で throw new Error() を使ってエラーを発生させています。すると、そのエラーが catch で受け取られます。

初心者の方は、ここを「サーバーの返事が失敗だったら、自分で失敗として扱う」と理解するとわかりやすいです。

エラーメッセージとは何か

次に学ぶのがエラーメッセージです。エラーメッセージとは、何かがうまくいかなかったときに、そのことを伝えるための文言です。ただし、誰に向けたメッセージなのかを意識することがとても大切です。

大きく分けると、次の2種類があります。

  • ユーザー向けのエラーメッセージ
  • 開発者向けの確認情報

たとえば、ユーザー向けには「データの取得に失敗しました。しばらくしてからもう一度お試しください。」のようなやさしい案内が向いています。一方で、開発者向けには console.log(error) のように、詳しい情報をコンソールへ出すことが役立ちます。

初心者の方は、「画面に出す言葉」と「コンソールで確認する情報」は役割が違うと理解すると、とても整理しやすいです。

良いエラーメッセージと悪いエラーメッセージ

ここも重要です。良いエラーメッセージは、ユーザーが状況を理解しやすく、必要以上に不安にならないものです。悪いエラーメッセージは、意味があいまいだったり、逆に内部情報を出しすぎたりするものです。

  • 良い例:「データの取得に失敗しました。時間をおいてもう一度お試しください。」
  • 悪い例:「Error: NetworkError when attempting to fetch resource」
  • 悪い例:「予期しない例外が発生しました」

最初の良い例は、何が起きたかと次にどうすればよいかが伝わります。一方、後ろの例は、ユーザーにとって難しすぎたり、何をすればよいかわからなかったりします。初心者の方は、「メッセージは相手に合わせる」ことを意識するとよいです。

ローディング表示との組み合わせが重要

Day 62で学んだローディング表示と、Day 63のエラー処理は必ずセットで考えるべきです。なぜなら、エラーが起きてもローディング表示は消さなければならないからです。

たとえば、次のような形が基本になります。

async function loadData() {
  try {
    loading.style.display = "block";
    message.textContent = "データを取得中です...";

    const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");

    if (!response.ok) {
      throw new Error("データを取得できませんでした。");
    }

    const data = await response.json();
    message.textContent = data.title;
  } catch (error) {
    message.textContent = "データの取得に失敗しました。";
    console.log(error);
  }

  loading.style.display = "none";
}
JavaScript

このようにすると、成功でも失敗でも最後にローディングが消えます。初心者の方は、「エラー処理はローディング解除まで含めて完成」と考えると実践的です。

ユーザー向け表示と console.log の両方を使う理由

ここは大事な設計ポイントです。初心者の方は、どちらか片方だけでよいと思うことがあります。しかし実際には、画面表示とコンソール確認の両方があると便利です。

  • 画面表示はユーザーのためです。
  • コンソール出力は開発者の確認のためです。

たとえば、画面には「データの取得に失敗しました。」とだけ出し、コンソールには error の中身を出すと、使う人にも開発者にもわかりやすくなります。初心者の方は、この役割分担を覚えておくと今後とても役立ちます。

そのまま使える完成テンプレート

ここでは、Day 63の内容をまとめた完成テンプレートを紹介します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Day 63 APIエラー処理</title>
  <style>
    /* ローディング表示の見た目 */
    #loading {
      display: none;
      color: blue;
      font-weight: bold;
    }

    /* エラーメッセージの見た目 */
    .error {
      color: red;
    }
  </style>
</head>
<body>
  <h2>APIエラー処理の確認</h2>

  <button id="loadButton">データを取得する</button>

  <p id="loading">読み込み中です...</p>
  <p id="message">ボタンを押すとデータを取得します。</p>

  <script>
    // 必要な要素を取得する
    const loadButton = document.getElementById("loadButton");
    const loading = document.getElementById("loading");
    const message = document.getElementById("message");

    // APIデータを取得する関数
    async function loadData() {
      try {
        // ローディング表示を出す
        loading.style.display = "block";

        // メッセージを初期化する
        message.textContent = "データを取得中です...";
        message.classList.remove("error");

        // APIへリクエストを送る
        const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");

        // HTTPエラーを自分で判定する
        if (!response.ok) {
          throw new Error("サーバーから正常な応答が返りませんでした。");
        }

        // JSONデータを読み取る
        const data = await response.json();

        // 取得成功時の表示
        message.textContent = "取得成功: " + data.title;
      } catch (error) {
        // エラー時の表示
        message.textContent = "データの取得に失敗しました。時間をおいて再試行してください。";
        message.classList.add("error");

        // 開発者向けに詳細を出力
        console.log("エラー詳細:", error);
      }

      // 成功でも失敗でもローディング表示を消す
      loading.style.display = "none";
    }

    // ボタン押下時に実行する
    loadButton.addEventListener("click", function () {
      loadData();
    });
  </script>
</body>
</html>

このテンプレートには、Day 63で押さえたい基本が入っています。

  • `try / catch` で失敗を受け止める
  • `response.ok` でHTTPエラーも確認する
  • 画面にはわかりやすいエラーメッセージを出す
  • コンソールには詳細を出す
  • ローディング表示を最後に必ず消す

意図的にエラーを試す方法

学習を深めるためには、成功パターンだけでなく失敗パターンも自分で試すことが大切です。初心者の方は、次のような方法でエラー状態を確認できます。

  • URLの一部をわざと間違える
  • 存在しないパスへアクセスする
  • ネットワークタブを見ながら挙動を確認する

たとえば、URLの末尾を todos/1 ではなく wrong-path に変えてみるだけでも、エラー処理の動きを確認しやすくなります。自分で失敗を起こして観察すると、try / catch の役割が実感しやすいです。

情報セキュリティの観点での注意

エラー処理では、何を画面に出すかがとても重要です。内部的なエラー内容をそのまま画面へ出すと、ユーザーには難しすぎるだけでなく、場合によってはシステム内部の情報を不用意に見せることにもつながります。学習段階では大きな問題になりにくいですが、基本姿勢としては、「画面にはわかりやすく簡潔な案内」「詳細は開発者が確認できる場所へ」という分け方が安全です。初心者の方は、便利さだけでなく、見せる情報の量にも注意する習慣をつけると、より安全な開発感覚が育ちます。

初心者が特に注意したいポイント

Day 63では、次の点を特に意識すると理解が深まりやすいです。

  • API通信は失敗することが普通にあります。
  • `try / catch` は失敗を受け止めるための基本です。
  • `fetch()` では `response.ok` の確認も重要です。
  • ユーザー向けメッセージと開発者向け確認情報は分けるとよいです。
  • エラー時にもローディング表示を必ず解除する必要があります。

特に重要なのは、「成功時のコードだけで完成ではない」という点です。失敗時の動きまで考えて初めて、実用的なAPI処理になります。

Day 63のゴールを整理する

Day 63では、次のことができるようになっていれば十分です。

  • 通信エラーとは何かを説明できる
  • `try / catch` の役割を説明できる
  • API通信で失敗したときの処理を書ける
  • わかりやすいエラーメッセージを画面に表示できる
  • `response.ok` を使ってHTTPエラーを確認できる
  • 成功時も失敗時もローディング表示を正しく切り替えられる

ここまでできれば、Day 63としてはとても順調です。API通信を成功時だけでなく、失敗時まで含めて設計できるようになっています。

まとめ

Day 63では、第5フェーズ「非同期処理とAPI」の実践として、APIエラー処理を学びました。通信エラーとは、ネットワーク不調、URL間違い、サーバー側の問題などによって、APIとのやり取りがうまくいかない状態です。こうした失敗は珍しいものではなく、最初から起こる前提で考えることが大切です。そのために使うのが try / catch であり、成功するかもしれない処理を try に書き、失敗したときの対応を catch に書きます。また、fetch() では通信自体の失敗だけでなく、response.ok を使ってサーバーの返事が正常かどうかも確認する必要があります。

さらに、エラーが起きたときには、ユーザー向けにはわかりやすい案内を画面に表示し、詳細な確認情報はコンソールへ出すという役割分担が大切です。そして、Day 62で学んだローディング処理とも組み合わせて、成功時でも失敗時でも最後にローディング表示を必ず消すことが重要です。ここまで意識できると、単に動くコードではなく、失敗にも強い実用的なコードへ近づいていきます。

次に進む前に、今回のテンプレートでわざとURLを間違え、エラー時に画面とコンソールがどう変わるかを確認してみるのがおすすめです。そして、「どの情報をユーザーに見せ、どの情報を開発者確認用に残すべきか」を自分の言葉で説明できるようになると、Day 63の理解がかなり深まっています。

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