5日目のゴールと今日やること
5日目のテーマは
「API 通信の“型”を保ったまま、アプリとしての使い勝手を一段上げる」
ことです。
ここまでであなたはすでに、
fetch と async/await で API からデータを取得する
try-catch と response.ok、data.error でエラーを分岐する
ローディング表示とボタン制御を入れる
forecast API で複数日の天気を表示する
アイコン表示や単位切り替え(℃ / ℉)を実装する
という「API 通信アプリの中核」を身につけました。
5日目はここに、
検索履歴の表示
履歴からの再検索
お気に入り都市の固定ボタン
エラー時・成功時の“再利用しやすいパターン化”
を加えて、
「何度も使いたくなる天気アプリ」に近づけていきます。
今日のアプリのイメージを先に固める
どんな機能を足すか
5日目で目指すアプリは、こんな動きをします。
都市名を入力して検索すると、いつも通り API から天気を取得する
検索した都市が「検索履歴」として下に溜まっていく
履歴の都市名をクリックすると、その都市で再検索できる
特に気に入った都市は「★ お気に入り」として登録できる
お気に入りはボタン一発で呼び出せる
ここで大事なのは、
fetch・async/await・エラーハンドリング・ローディングの“型”は変えずに、
「その周りに機能を足していく」という感覚です。
fetch と async/await は「もうフォームとして固定する」
まずは基本の非同期関数を再確認する
API 通信の中心は、すでにこの形になっています。
async function fetchForecast(city) {
const error = validateCity(city);
if (error) {
showError(error);
clearResult();
return;
}
showLoading();
startLoading();
try {
const url = buildUrl(city, 3);
const response = await fetch(url);
if (!response.ok) {
showError(`サーバーエラーが発生しました。(${response.status})`);
return;
}
const data = await response.json();
if (data.error) {
showError(`エラー:${data.error.message}`);
return;
}
showStatus("3日分の天気を取得しました。");
renderForecast(data);
addHistory(city);
} catch (error) {
showError("通信に失敗しました。ネットワークを確認してください。");
console.error(error);
} finally {
endLoading();
}
}
JavaScriptここで意識してほしいのは、
「この形はもう“完成されたフォーム”として扱っていい」
ということです。
今日やるのは、
この中に addHistory(city) のような
「周辺機能」を差し込んでいくことです。
検索履歴を実装してみる
履歴をどこに保存するか
まずはシンプルに、
メモリ上の配列で管理します。
const history = [];
JavaScript検索が成功したタイミングで、
履歴に都市名を追加します。
function addHistory(city) {
if (history.includes(city)) {
return;
}
history.unshift(city);
if (history.length > 5) {
history.pop();
}
renderHistory();
}
JavaScriptここでは、
同じ都市を重複して入れない
新しいものを先頭に入れる
最大 5 件までに制限する
というルールを入れています。
履歴を画面に表示する
HTML 側に履歴表示用のエリアを用意します。
<div id="history"></div>
JavaScript で描画します。
const historyDiv = document.getElementById("history");
function renderHistory() {
if (history.length === 0) {
historyDiv.textContent = "検索履歴はまだありません。";
return;
}
let html = "<p>検索履歴:</p>";
history.forEach((city) => {
html += `<button class="history-item" data-city="${city}">${city}</button>`;
});
historyDiv.innerHTML = html;
const buttons = historyDiv.querySelectorAll(".history-item");
buttons.forEach((btn) => {
btn.addEventListener("click", () => {
const city = btn.dataset.city;
cityInput.value = city;
fetchForecast(city);
});
});
}
JavaScriptこれで、
検索が成功するたびに履歴が更新され、
履歴のボタンを押すとその都市で再検索できるようになります。
深掘りポイント
ここでやっていることは、
実は API 通信とはまったく別の話です。
配列に値を追加する
配列をループしてボタンを作る
ボタンにイベントを付ける
という、
「純粋なフロントエンドのロジック」です。
でも、fetchForecast の中で addHistory(city) を呼ぶことで、
API 通信と履歴機能が自然につながっています。
お気に入り都市を実装してみる
お気に入りの状態を持つ
お気に入りは 1 件だけにしてみましょう。
let favoriteCity = null;
JavaScriptHTML に「お気に入り」ボタンを用意します。
<button id="favoriteButton">★ お気に入りに登録</button>
<button id="favoriteJumpButton">★ お気に入りを表示</button>
JavaScript で制御します。
const favoriteButton = document.getElementById("favoriteButton");
const favoriteJumpButton = document.getElementById("favoriteJumpButton");
favoriteButton.addEventListener("click", () => {
const city = cityInput.value.trim();
if (!city) {
showError("お気に入りに登録するには都市名を入力してください。");
return;
}
favoriteCity = city;
showStatus(`「${city}」をお気に入りに登録しました。`);
updateFavoriteUI();
});
favoriteJumpButton.addEventListener("click", () => {
if (!favoriteCity) {
showError("お気に入りがまだ登録されていません。");
return;
}
cityInput.value = favoriteCity;
fetchForecast(favoriteCity);
});
JavaScriptお気に入りの状態に応じて UI を変えます。
function updateFavoriteUI() {
if (favoriteCity) {
favoriteJumpButton.disabled = false;
favoriteJumpButton.textContent = `★ ${favoriteCity} を表示`;
} else {
favoriteJumpButton.disabled = true;
favoriteJumpButton.textContent = "★ お気に入りを表示";
}
}
JavaScript初期化時に一度呼んでおきます。
updateFavoriteUI();
JavaScript深掘りポイント
ここで重要なのは、
「状態を変数として持ち、それに応じて UI を更新する」
というパターンです。
favoriteCity という状態
updateFavoriteUI という「状態 → UI」の関数
この組み合わせは、
タイマーの state や
ローディングの isLoading と
まったく同じ構造をしています。
エラーハンドリングを「パターン化」して再利用しやすくする
エラー表示の共通関数を整える
すでに showError や showStatus を使っていましたが、
ここで一度「パターン」として整理しておきます。
function setStatus(message, type) {
statusDiv.textContent = message;
statusDiv.className = "status " + type;
}
function showError(message) {
setStatus(message, "error");
}
function showStatus(message) {
setStatus(message, "success");
}
function showLoading() {
setStatus("天気情報を取得中です…", "loading");
}
JavaScriptこれにより、
fetchForecast の中はかなり読みやすくなります。
async function fetchForecast(city) {
const error = validateCity(city);
if (error) {
showError(error);
clearResult();
return;
}
showLoading();
startLoading();
try {
const url = buildUrl(city, 3);
const response = await fetch(url);
if (!response.ok) {
showError(`サーバーエラーが発生しました。(${response.status})`);
return;
}
const data = await response.json();
if (data.error) {
showError(`エラー:${data.error.message}`);
return;
}
showStatus("3日分の天気を取得しました。");
renderForecast(data);
addHistory(city);
} catch (error) {
showError("通信に失敗しました。ネットワークを確認してください。");
console.error(error);
} finally {
endLoading();
}
}
JavaScript深掘りポイント
ここでやっているのは、
「エラーハンドリングの書き方を“型”として固定する」
ということです。
通信エラー → catch で showError
HTTP エラー → !response.ok で showError
API エラー → data.error で showError
この 3 段階を、
どの API でも同じように書けるようになると、
新しい API を触るときの不安が一気に減ります。
ローディング表示と履歴・お気に入りの関係を整理する
ローディング中に「履歴ボタン」や「お気に入りボタン」はどうするか
厳密にやるなら、
ローディング中は履歴ボタンやお気に入りボタンも無効化する
という設計もあります。
ただ、5日目の段階では、
まずは「検索ボタンだけ無効化」で十分です。
重要なのは、
「ローディング中に同じ API を何度も叩かない」
ことです。
履歴やお気に入りは、
「検索のきっかけ」を増やす機能なので、
基本的には fetchForecast を呼ぶだけです。
もし余裕があれば、
startLoading の中で履歴ボタンも無効化し、
endLoading で戻す、
という拡張も考えられます。
5日目の全体像をまとめる
5日目でやったことを整理すると、こうなります。
fetch・async/await・エラーハンドリングの“型”はそのまま
検索成功時に addHistory(city) を呼んで履歴を更新
履歴は配列で管理し、ボタンとして表示して再検索に使う
お気に入り都市を favoriteCity として状態管理し、ボタンで呼び出せるようにする
エラー・成功・ローディング表示を setStatus でパターン化する
どれも「新しい文法」ではなく、
「状態管理 × DOM 更新 × 既存の fetch ロジック」
の組み合わせです。
今日いちばん深く理解してほしいこと
5日目の本質は、
API 通信そのものはもう十分に理解している
これからは「その結果をどう活かすか」を設計していく段階にいる
ということです。
検索履歴
お気に入り
状態に応じた UI
エラーハンドリングのパターン化
これらはすべて、
「ユーザーが何度も使いたくなるアプリ」に近づけるための工夫です。
あなたはもう、
「API を叩いてデータを表示する人」ではなく、
“API を使ったアプリ体験を設計する人”になりつつあります。
次の 6 日目では、
この天気アプリに「位置情報(現在地の天気)」や
「複数都市の比較」などを加えて、
さらに一段階上の中級レベルに踏み込んでいきましょう。

