3日目のゴールと今日やること
3日目のテーマは
「祝日アプリを“使っていて気持ちいいツール”に近づける」 ことです。
技術の柱は変わりません。
- fetch
- Promise / async-await
- エラーハンドリング
ただ、今日はそれを
- 「今年」ボタン
- 「前の年」「次の年」ボタン
- 国プリセットを意識した UI
- それらにきれいに対応する fetch とエラーハンドリング
に結びつけていきます。
新しい文法はほぼ出てきません。
2日目までに作った「型」を、どう応用するかがテーマです。
2日目までの祝日アプリをざっくり整理する
あなたのアプリは、すでにこんな流れを持っています。
- 年を入力
- 国コードを選択
- 「祝日を取得」ボタンを押す
- fetch で Nager.Date API にアクセス
- async/await で結果を待つ
- try/catch でエラーを受け止める
- HTTP ステータスや配列かどうかをチェック
- ローディング表示を出す・戻す
今日はここに「年を動かす操作」を足していきます。
今年ボタン・前後の年ボタンを考える
UI のイメージ
HTML はこんな感じを想像してください。
<input id="yearInput" type="number" placeholder="年を入力(例: 2025)" />
<button id="thisYearButton">今年</button>
<button id="prevYearButton">前年</button>
<button id="nextYearButton">翌年</button>
<select id="countrySelect">
<option value="JP">日本 (JP)</option>
<option value="US">アメリカ (US)</option>
<option value="DE">ドイツ (DE)</option>
</select>
<button id="fetchButton">祝日を取得</button>
<div id="status"></div>
<div id="result"></div>
JavaScript 側では、これらを取得します。
const yearInput = document.getElementById("yearInput");
const countrySelect = document.getElementById("countrySelect");
const fetchButton = document.getElementById("fetchButton");
const thisYearButton = document.getElementById("thisYearButton");
const prevYearButton = document.getElementById("prevYearButton");
const nextYearButton = document.getElementById("nextYearButton");
const statusDiv = document.getElementById("status");
const resultDiv = document.getElementById("result");
JavaScript「今年」をセットする関数を作る
Date オブジェクトから今年を取る
JavaScript の Date を使えば、
現在の年を簡単に取れます。
function getCurrentYear() {
return new Date().getFullYear();
}
JavaScriptこれを使って、「今年」ボタンの動きを決めます。
function setThisYear() {
const year = getCurrentYear();
yearInput.value = year;
statusDiv.textContent = `${year}年を選択しました。`;
}
JavaScriptイベント登録はこうです。
thisYearButton.addEventListener("click", setThisYear);
JavaScriptここではまだ fetch は呼びません。
「年をセットする」と「祝日を取得する」を分けておくと、
コードが整理しやすくなります。
前の年・次の年ボタンのロジック
年を増減させる小さな関数
まず、「今入力欄に入っている年」を安全に数値に変換する関数を作ります。
function getYearFromInput() {
const raw = yearInput.value.trim();
const year = Number(raw);
if (!Number.isInteger(year)) {
return null;
}
return year;
}
JavaScriptこれを使って、前年・翌年をこう書きます。
function setPrevYear() {
const current = getYearFromInput() ?? getCurrentYear();
const nextYear = current - 1;
yearInput.value = nextYear;
statusDiv.textContent = `${nextYear}年を選択しました。`;
}
function setNextYear() {
const current = getYearFromInput() ?? getCurrentYear();
const nextYear = current + 1;
yearInput.value = nextYear;
statusDiv.textContent = `${nextYear}年を選択しました。`;
}
JavaScriptイベント登録はこうです。
prevYearButton.addEventListener("click", setPrevYear);
nextYearButton.addEventListener("click", setNextYear);
JavaScriptここでも、まだ fetch は呼びません。
「年を動かす」と「API を叩く」を分離しておくのがポイントです。
年のバリデーションを再利用できる形にする
2日目の parseYear を活かす
2日目で作った parseYear を、3日目でもそのまま使います。
function parseYear(raw) {
const year = Number(raw);
if (!Number.isInteger(year)) {
return { ok: false, message: "年は数字で入力してください。" };
}
if (year < 1900 || year > 2100) {
return { ok: false, message: "年は 1900〜2100 の範囲で入力してください。" };
}
return { ok: true, value: year };
}
JavaScriptfetchHolidays の最初はこうなります。
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;
// ここから先は「year はちゃんとした数値」として扱える
}
JavaScriptここまでで、
- 年をセットする(今年・前年・翌年)
- 年をチェックする(parseYear)
という「入力まわりの責任」がきれいに分かれました。
ローディング表示を状態として扱う
isLoading を持つ
2日目で作った setLoading を、3日目でも使います。
const state = {
isLoading: false
};
function setLoading(isLoading, message) {
state.isLoading = isLoading;
if (isLoading) {
statusDiv.textContent = message || "取得中です…";
fetchButton.disabled = true;
thisYearButton.disabled = true;
prevYearButton.disabled = true;
nextYearButton.disabled = true;
} else {
fetchButton.disabled = false;
thisYearButton.disabled = false;
prevYearButton.disabled = false;
nextYearButton.disabled = false;
}
}
JavaScriptポイントは、
「ローディング中は年を動かすボタンも無効にする」ことです。
API 通信中に年を変えられると、
ユーザーが「どの年の結果かわからない」と感じることがあります。
fetch / async-await / エラーハンドリングの“型”を固める
3日目版 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 url = `https://date.nager.at/api/v3/PublicHolidays/${year}/${countryCode}`;
const response = await fetch(url);
if (!response.ok) {
if (response.status === 404) {
statusDiv.textContent = "指定された年または国の祝日が見つかりませんでした。";
} else if (response.status >= 500) {
statusDiv.textContent = "サーバー側でエラーが発生しています。時間をおいて再試行してください。";
} else {
statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
}
return;
}
const data = await response.json();
if (!Array.isArray(data)) {
statusDiv.textContent = "予期しない形式のデータが返されました。";
console.error("Unexpected data:", data);
return;
}
if (data.length === 0) {
statusDiv.textContent = "祝日が見つかりませんでした。";
resultDiv.textContent = "";
return;
}
statusDiv.textContent = "祝日の取得に成功しました。";
renderHolidays(data);
} catch (error) {
statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
console.error(error);
} finally {
setLoading(false);
}
}
JavaScriptここに、fetch / async-await / エラーハンドリングの「型」が全部入っています。
重要ポイント1:try の中は「成功したときのストーリー」にする
try ブロックの中は、
できるだけ「うまくいったときの流れ」だけを書くようにします。
- URL を組み立てる
- fetch でレスポンスを取る
- HTTP ステータスをチェックする
- JSON に変換する
- 配列かどうかチェックする
- 件数 0 なら「該当なし」
- それ以外なら表示する
失敗したときのことは、
HTTP エラーの if ブロックと catch に任せます。
こうすると、
コードが「物語」として読めるようになります。
重要ポイント2:エラーの“責任の場所”を意識する
この関数の中には、いろんなエラーがあります。
- 入力が不正 → parseYear の責任
- HTTP エラー → response.ok のチェック
- データ形式がおかしい → Array.isArray のチェック
- 該当なし → length === 0 のチェック
- ネットワークエラー → catch
それぞれの責任が、
どこで扱われているかがはっきりしていると、
あとから読むときにとても楽です。
祝日一覧の表示を少しだけ改善する
年と国コードをタイトルに出す
2日目の renderHolidays を、3日目でも使います。
function renderHolidays(data) {
if (data.length === 0) {
resultDiv.textContent = "";
return;
}
const countryCode = data[0].countryCode;
const year = data[0].date.slice(0, 4);
let html = `<h3>${year}年 ${countryCode} の祝日一覧</h3>`;
data.forEach((item) => {
html += `<p>${item.date}:${item.localName}(${item.name})</p>`;
});
resultDiv.innerHTML = html;
}
JavaScriptこれで、
- 「今どの年・どの国を見ているか」
- 「ボタンで年を動かした結果がどう反映されているか」
が一目でわかるようになります。
ボタンと fetch をどうつなぐか
「年を変える」と「取得する」を分ける設計
今の設計では、
- 今年ボタン → 年だけ変える
- 前年・翌年ボタン → 年だけ変える
- 祝日を取得ボタン → 年と国を使って API を叩く
という分担になっています。
もし「年を変えたら自動で取得したい」と思ったら、
こう書くこともできます。
function setPrevYearAndFetch() {
setPrevYear();
fetchHolidays();
}
prevYearButton.addEventListener("click", setPrevYearAndFetch);
JavaScriptでも、3日目の段階では
「年を変える」と「取得する」を分けておいた方が、
コードの責任がはっきりしていて学びやすいです。
3日目のまとめ
今日あなたがやったことを、言葉で整理してみます。
- 今年ボタンで、現在の年を入力欄にセットするようにした
- 前年・翌年ボタンで、入力欄の年を増減できるようにした
- 年の取得とバリデーションを関数に分けて整理した
- ローディング状態を state として持ち、年を動かすボタンも含めて一括で無効化するようにした
- fetch / async-await / try-catch の「型」を、祝日アプリの中で固めた
- エラーの責任(入力・HTTP・データ形式・該当なし・ネットワーク)を、コード上で分けて扱った
- 表示に年と国コードを含めて、UI と内部状態のつながりをわかりやすくした
どれも新しい文法ではなく、
「同じ型をどう応用するか」「責任をどう分けるか」 の話です。
今日いちばん深く理解してほしいこと
3日目の本質は、
「fetch / async-await / エラーハンドリングは、“アプリのストーリー”の中に溶け込ませるもの」
ということです。
年を動かす
国を選ぶ
祝日を取得する
結果を表示する
失敗したら理由を伝える
この一連の流れの中に、
fetch と Promise とエラーハンドリングが自然に組み込まれている。
ここまで来ているあなたは、
もう「API を叩くコードを書く人」ではなく、
「API を使ったアプリを設計する人」 の側に足を踏み入れています。
4日目では、この祝日アプリに
「お気に入りの祝日」「ローカルストレージ保存」「状態管理の整理」
などを足して、さらに“毎日使えるツール”に育てていきます。


