- 2日目のゴールと今日やること
- Datamuse API の「モード」をちゃんと理解する
- 1日目のコードを「2日目仕様」にアップデートする
- fetch の「型」を少しだけ整える
- エラーハンドリングを 3 段階で考える
- ローディング表示を「ちゃんとしたパターン」にする
- モードごとにメッセージを変える小さな工夫
- 2日目版 fetchWords の全体像
- 重要ポイント1:encodeURIComponent を使う理由
- 重要ポイント2:Array.isArray で形式をチェックする
- 重要ポイント3:「該当なし」と「エラー」を分ける
- 重要ポイント4:catch では「技術用語をそのまま出さない」
- 結果表示を少しだけリッチにする
- イベント登録は 1 行でシンプルに
- 2日目のまとめ
- 今日いちばん深く理解してほしいこと
2日目のゴールと今日やること
2日目のテーマは
「1日目で作った Datamuse ミニアプリを、“ちゃんと使えるツール”に近づける」 ことです。
1日目では、
単語を入力 → モードを選ぶ → Datamuse API に fetch → 結果を表示
という「最低限動く」アプリを作りました。
2日目では、そこから一歩進めて、
- fetch と async/await を“パターン”として固める
- エラーハンドリングを少し丁寧にする
- ローディング表示を改善する
- 検索モードを増やして「アプリらしさ」を出す
というところを狙っていきます。
「新しい文法を覚える」というより、
昨日やったことを“設計として整理する” イメージです。
Datamuse API の「モード」をちゃんと理解する
よく使う 3 つのモード
Datamuse API は、クエリパラメータによって動きが変わります。
中級編でよく使うのは、このあたりです。
ml=
意味が近い単語(類義語)を返す。
例:?ml=happy
rel_trg=
その単語から連想される単語を返す。
例:?rel_trg=dog
rel_rhy=
韻を踏む単語を返す。
例:?rel_rhy=time
1日目では ?ml=word のように 1 パターンだけ使いましたが、
2日目では「モードを切り替えられる UI」を前提にして考えます。
1日目のコードを「2日目仕様」にアップデートする
HTML のイメージ(モードを増やす)
モード選択を、こういう感じにしておきます。
<input id="wordInput" placeholder="単語を入力" />
<select id="modeSelect">
<option value="ml">意味が近い単語(類義語)</option>
<option value="rel_trg">連想される単語</option>
<option value="rel_rhy">韻を踏む単語</option>
</select>
<button id="searchButton">検索</button>
<div id="status"></div>
<div id="result"></div>
JavaScript 側では、modeSelect.value が ml / rel_trg / rel_rhy のいずれかになります。
fetch の「型」を少しだけ整える
1日目の fetch を思い出す
1日目では、だいたいこんな形でした。
const url = `https://api.datamuse.com/words?${mode}=${word}`;
const response = await fetch(url);
const data = await response.json();
JavaScriptこれでも動きますが、
2日目では「エラーのこともちゃんと考えた fetch」にしていきます。
エラーハンドリングを 3 段階で考える
API 通信のエラーは、ざっくり 3 つに分けられます。
1つ目は、ネットワークエラー。
Wi-Fi が切れている、サーバーにそもそも届かない、などのケースです。
これは fetch 自体が失敗して catch に入ります。
2つ目は、HTTP エラー。
サーバーには届いたけれど、ステータスコードが 200 ではない場合です。response.ok が false になります。
3つ目は、データの中身が想定外。
JSON は返ってきたけれど、配列じゃない、空配列だった、必要なプロパティがない、などです。
この 3 段階を意識して、
「どこで何をチェックするか」 を整理していきます。
ローディング表示を「ちゃんとしたパターン」にする
startLoading / endLoading を少し強化する
1日目では、こんな感じでした。
function startLoading() {
statusDiv.textContent = "取得中です…";
searchButton.disabled = true;
}
function endLoading() {
searchButton.disabled = false;
}
JavaScript2日目では、「メッセージを変えられるようにする」と便利です。
function startLoading(message) {
statusDiv.textContent = message || "取得中です…";
searchButton.disabled = true;
}
function endLoading() {
searchButton.disabled = false;
}
JavaScriptこうしておくと、
「類義語を取得中…」「連想語を取得中…」など、
モードに応じたメッセージを出せるようになります。
モードごとにメッセージを変える小さな工夫
モード名を人間向けのラベルに変換する
API のパラメータ ml や rel_trg は、
人間にはわかりにくいので、
「表示用の名前」に変換する関数を作ります。
function getModeLabel(mode) {
if (mode === "ml") return "意味が近い単語";
if (mode === "rel_trg") return "連想される単語";
if (mode === "rel_rhy") return "韻を踏む単語";
return "関連語";
}
JavaScriptこれを使うと、
ステータスメッセージが一気にわかりやすくなります。
2日目版 fetchWords の全体像
ここからが今日のメインです。
1日目の fetchWords を、2日目仕様に書き直してみます。
async function fetchWords() {
const word = wordInput.value.trim();
const mode = modeSelect.value;
if (!word) {
statusDiv.textContent = "単語を入力してください。";
resultDiv.textContent = "";
return;
}
const modeLabel = getModeLabel(mode);
startLoading(`${modeLabel}を取得中です…`);
resultDiv.textContent = "";
try {
const url = `https://api.datamuse.com/words?${mode}=${encodeURIComponent(word)}`;
const response = await fetch(url);
if (!response.ok) {
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 = `${modeLabel}が見つかりませんでした。`;
resultDiv.textContent = "";
return;
}
statusDiv.textContent = `${modeLabel}の取得に成功しました。`;
renderWords(data, modeLabel);
} catch (error) {
statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
console.error(error);
} finally {
endLoading();
}
}
JavaScriptここで、重要なポイントをひとつずつ分解していきます。
重要ポイント1:encodeURIComponent を使う理由
const url = `https://api.datamuse.com/words?${mode}=${encodeURIComponent(word)}`;
JavaScriptここで encodeURIComponent を使っているのは、
ユーザーがスペースや日本語を入力したときに、
URL として正しく送るためです。
例えば、ice cream や ありがとう のような文字列は、
そのままだと URL として不正になる可能性があります。
encodeURIComponent は、
「URL の中で特別な意味を持つ文字を、安全な形に変換する」
ための関数です。
重要ポイント2:Array.isArray で形式をチェックする
if (!Array.isArray(data)) {
statusDiv.textContent = "予期しない形式のデータが返されました。";
console.error("Unexpected data:", data);
return;
}
JavaScriptDatamuse API は、通常は配列を返します。
しかし、何かの理由で仕様が変わったり、
プロキシや別のサーバーを経由したりすると、
配列ではないものが返ってくる可能性もゼロではありません。
ここで形式をチェックしておくと、
「おかしなデータが来たときに、原因を追いやすくなる」
というメリットがあります。
重要ポイント3:「該当なし」と「エラー」を分ける
if (data.length === 0) {
statusDiv.textContent = `${modeLabel}が見つかりませんでした。`;
resultDiv.textContent = "";
return;
}
JavaScriptここがとても大事です。
「データが 0 件だった」というのは、
エラーではなく“正常な結果” です。
- 通信は成功している
- サーバーも正常に動いている
- ただ、その単語に対応する結果がなかった
というだけなので、
「エラーが起きました」ではなく、
「見つかりませんでした」と伝えるのが正解です。
重要ポイント4:catch では「技術用語をそのまま出さない」
} catch (error) {
statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
console.error(error);
}
JavaScriptここで、statusDiv.textContent = error.message;
のようにしてしまうと、
ユーザーには意味がわからない英語のメッセージが出ることがあります。
ユーザーに見せるメッセージは、
「状況がイメージできる日本語」 にするのが大事です。
一方で、console.error(error);
で開発者向けの情報はちゃんと残しておきます。
結果表示を少しだけリッチにする
スコア順に並べて表示する
Datamuse のレスポンスには score という数値が入っています。
これは「どれくらい関連度が高いか」の指標です。
2日目では、
この score を使って並べ替えてみましょう。
function renderWords(data, modeLabel) {
const sorted = [...data].sort((a, b) => (b.score || 0) - (a.score || 0));
let html = `<h3>${modeLabel}の結果</h3>`;
sorted.forEach((item) => {
const scoreText = item.score != null ? `(スコア: ${item.score})` : "";
html += `<p>${item.word}${scoreText}</p>`;
});
resultDiv.innerHTML = html;
}
JavaScriptここでのポイントは、[...data] として元の配列をコピーしてから sort していることです。
元の配列を直接いじらないことで、
「副作用の少ないコード」になります。
イベント登録は 1 行でシンプルに
searchButton.addEventListener("click", fetchWords);
JavaScriptここは 1日目と同じですが、
「ボタンを押したら何が起きるか」が
1 行で読み取れる のは、とても大事です。
2日目のまとめ
2日目でやったことを、言葉で整理してみます。
Datamuse API のモードを理解して、ml / rel_trg / rel_rhy を UI から切り替えられるようにした。
fetch の前後に、
- 入力チェック
- ローディング開始
- HTTP ステータスチェック
- データ形式チェック
- 件数 0 のときの分岐
- 成功時の表示
- 通信失敗時のメッセージ
- ローディング終了
という「一連の流れ」をきれいに並べた。
エラーハンドリングを、
- ネットワークエラー(catch)
- HTTP エラー(response.ok)
- データ形式エラー(Array.isArray)
- 該当なし(length === 0)
に分けて考えた。
そして、
「ユーザーに見せるメッセージ」と「開発者が見るログ」を分けた。
これらは全部、
fetch / async-await / try-catch を
「雑に書く」のではなく
「設計として扱い始めた」 ということです。
今日いちばん深く理解してほしいこと
2日目の本質は、
「同じ fetch でも、“どう扱うか”でアプリの質が変わる」
ということです。
まったく同じ Datamuse API を叩いていても、
- エラーと該当なしを分けているか
- モード名を人間向けに変換しているか
- ローディング表示があるか
- メッセージが日本語でわかりやすいか
これだけで、
「ただ動くだけのサンプル」と
「ちゃんとしたアプリ」の差が生まれます。
3日目では、
この Datamuse アプリに「入力補助」「サジェスト」「履歴」などを足して、
さらに“アプリらしさ”を育てていきます。


