JavaScript | 1 日 120 分 × 7 日アプリ学習:API通信アプリ(Nager.Date API)

APP JavaScript
スポンサーリンク

6日目のゴールと今日やること

6日目のテーマは
「Nager.Date API 祝日アプリを“読みやすく・直しやすく・拡張しやすい設計”に進化させる」
ことです。

ここまでであなたは、

  • 年・国を指定して祝日取得
  • ローディング表示
  • エラーハンドリング
  • 前年・翌年・今年ボタン
  • お気に入り機能
  • localStorage 保存
  • 複数国比較(Promise.all)

といった、API アプリの本質をすでに体験しています。

6日目は、これらを 「設計として整理する」 回です。

つまり、
fetch / async‑await / エラーハンドリングを“アプリ全体の型”として扱えるようにする
ことが目的です。


状態管理(state)をアプリの中心に置く

なぜ state を中心にするのか

昨日までのコードでは、
祝日一覧・お気に入り・ローディング状態などが
バラバラの変数として存在していました。

これをひとつの state にまとめると、

  • どこに何があるか迷わない
  • UI 更新がしやすい
  • 保存(localStorage)と連動しやすい
  • デバッグがしやすい

というメリットがあります。

state の基本形

const state = {
  isLoading: false,
  holidays: [],
  favorites: [],
  comparison: []
};
JavaScript

状態を更新する関数

function updateState(updates) {
  Object.assign(state, updates);
}
JavaScript

この関数があるだけで、
アプリ全体のコードがスッキリします。


fetch を「共通関数」にまとめて再利用する

なぜ共通化が必要なのか

祝日アプリでは、

  • 単一国の祝日取得
  • 複数国比較
  • お気に入りからの再取得

など、fetch を使う場面が多いです。

そこで、
fetch のエラーハンドリングを共通化した関数
を作ります。

共通 fetch 関数(重要)

async function requestJson(url) {
  try {
    const response = await fetch(url);

    if (!response.ok) {
      if (response.status === 404) {
        throw new Error("データが見つかりませんでした。");
      }
      if (response.status >= 500) {
        throw new Error("サーバー側でエラーが発生しています。時間をおいて再試行してください。");
      }
      throw new Error(`HTTPエラー(${response.status})`);
    }

    const data = await response.json();
    return data;

  } catch (error) {
    throw new Error(error.message);
  }
}
JavaScript

深掘りポイント

この関数は、

  • fetch の失敗
  • HTTP エラー
  • JSON パースエラー

をすべて Error として上に投げる 役割を持っています。

これにより、
上位の関数は「何をしたいか」だけに集中できます。


Nager.Date 専用の関数を作る

祝日取得を「読みやすい関数」にする

async function fetchPublicHolidays(year, countryCode) {
  const url = `https://date.nager.at/api/v3/PublicHolidays/${year}/${countryCode}`;
  const data = await requestJson(url);

  if (!Array.isArray(data)) {
    throw new Error("予期しない形式のデータが返されました。");
  }

  if (data.length === 0) {
    throw new Error("祝日が見つかりませんでした。");
  }

  return data;
}
JavaScript

深掘りポイント

fetchPublicHolidays
「Nager.Date API の仕様を知っている唯一の関数」
です。

UI 側は API の細かい仕様を知らなくてよくなり、
コードが読みやすくなります。


UI 側の fetchHolidays を“ストーリーとして読める”形にする

6日目版 fetchHolidays(完成形)

async function fetchHolidays() {
  const rawYear = yearInput.value.trim();
  const countryCode = countrySelect.value;

  const parsed = parseYear(rawYear);
  if (!parsed.ok) {
    statusDiv.textContent = parsed.message;
    resultDiv.textContent = "";
    return;
  }

  const year = parsed.value;

  setLoading(true, `${year}年の祝日を取得中です…`);
  resultDiv.textContent = "";

  try {
    const holidays = await fetchPublicHolidays(year, countryCode);

    updateState({ holidays });
    statusDiv.textContent = "祝日の取得に成功しました。";
    renderHolidays(holidays);

  } catch (error) {
    statusDiv.textContent = `取得中にエラーが発生しました:${error.message}`;
    console.error(error);

  } finally {
    setLoading(false);
  }
}
JavaScript

深掘りポイント

この関数は、
「祝日取得の流れをそのまま読める」
という設計になっています。

  • 入力チェック
  • ローディング開始
  • fetchPublicHolidays で祝日取得
  • 成功時の表示
  • 失敗時のメッセージ
  • ローディング終了

fetch の細かい処理は
fetchPublicHolidaysrequestJson に隠れているため、
とても読みやすくなっています。


複数国比較を Promise.all で整理する

複数国比較の関数

async function fetchMultipleCountries(year, countries) {
  const promises = countries.map((code) =>
    fetchPublicHolidays(year, code)
  );

  const results = await Promise.all(promises);
  return results;
}
JavaScript

深掘りポイント

Promise.all を使うと、

  • 並列で API を叩ける
  • 全部終わるまで待てる
  • 結果をまとめて扱える

というメリットがあります。


ローディング表示を「状態」として扱う

state と連動させる

function setLoading(isLoading, message) {
  updateState({ isLoading });

  if (isLoading) {
    statusDiv.textContent = message || "処理中です…";
  }

  fetchButton.disabled = isLoading;
  compareButton.disabled = isLoading;
  thisYearButton.disabled = isLoading;
  prevYearButton.disabled = isLoading;
  nextYearButton.disabled = isLoading;
}
JavaScript

深掘りポイント

複数国比較でも、
単一国の取得でも、
ローディング表示が一貫して動く ようになります。


エラーハンドリングを「原因別」に整理する

どこで何を扱うか

  • 入力エラー → parseYear
  • HTTP エラー → requestJson
  • データ形式エラー → fetchPublicHolidays
  • 該当なし → fetchPublicHolidays
  • ネットワークエラー → catch

このように、
エラーの責任を関数ごとに分ける
ことで、コードが読みやすくなります。


6日目のまとめ

今日あなたがやったことを整理すると、こうなります。

  • fetch を requestJson に共通化
  • Nager.Date 専用の fetchPublicHolidays を作成
  • UI 側の fetchHolidays を「読みやすい流れ」に整理
  • Promise.all で複数国比較を整理
  • ローディング表示を state と連動
  • エラーハンドリングを原因別に整理
  • 状態管理(state)をアプリの中心に置いた

どれも新しい文法ではなく、
「fetch / async‑await / エラーハンドリングを“設計として扱う”」
という話です。


今日いちばん深く理解してほしいこと

6日目の本質は、

「コードは“動けばいい”から、“読めて直せて拡張できる”に育てていくもの」
ということです。

祝日アプリは、
fetch → async/await → try-catch → state → UI
という“型”の上に成り立っています。

7日目では、このアプリを
中級編の完成形としてまとめる
回に入ります。

あなたのアプリが「本当に使えるツール」になる瞬間です。

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