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

APP JavaScript
スポンサーリンク

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

NewsAPI.org 中級編 1 日目のテーマは
「ニュース API から記事一覧を安全に取得して、ちゃんと“待ち”と“失敗”を扱えるようになること」
です。

キーワードはこの 3 つです。

fetch
Promise / async-await
エラーハンドリング

実装としては、まずこのレベルを目指します。

API からニュース記事一覧を取得する
取得中は「読み込み中…」を表示する
失敗したら、ユーザーにわかるメッセージを出す

「天気」から「ニュース」に変わるだけで、
やることの本質は同じです。
だからこそ、“型”として身につけるチャンスです。


今日作るミニアプリのイメージ

どんな画面にするか

まずは、超シンプルなニュースアプリを作ります。

キーワードを入力する
「検索」ボタンを押す
NewsAPI.org から記事一覧を取得する
取得中は「読み込み中…」と表示する
成功したら「タイトル・説明・リンク」を一覧表示
失敗したら「エラーのメッセージ」を表示

HTML のイメージはこんな感じです。

<input id="keywordInput" placeholder="キーワードを入力 (例: JavaScript)" />
<button id="searchButton">ニュース検索</button>

<div id="status"></div>
<div id="result"></div>

status は「ローディングやエラー表示」用、
result は「ニュース記事の一覧表示」用です。


fetch を「サーバーにお願いしに行く関数」として理解する

fetch の一番シンプルな形

fetch は、
「この URL にアクセスして、結果をちょうだい」
とブラウザに頼む関数です。

基本形はこうです。

fetch("https://example.com/api")
  .then((response) => {
    console.log("レスポンスが返ってきた", response);
  })
  .catch((error) => {
    console.error("通信に失敗した", error);
  });
JavaScript

ここで大事なのは、
fetch は「すぐにデータそのもの」を返すのではなく、
「Promise(あとで結果がわかる箱)」を返す、という点です。


Promise と async/await をかみ砕いて理解する

Promise は「未来の結果の箱」

fetch を呼ぶと、
「今すぐ結果はないけど、終わったら教えるね」という
Promise が返ってきます。

then は「終わったらこれをしてね」
catch は「失敗したらこれをしてね」

という“予約”です。

fetch(url)
  .then((response) => {
    // 成功時
  })
  .catch((error) => {
    // 失敗時
  });
JavaScript

async/await は「同期っぽく書ける文法」

Promise を then でつなぐと、
ネストが深くなりがちなので、
よく使われるのが async/await です。

async function getData() {
  try {
    const response = await fetch(url);
    console.log("レスポンス", response);
  } catch (error) {
    console.error("エラー", error);
  }
}
JavaScript

await は
「この Promise が終わるまで、ここで一旦待つ」
という意味です。

その代わり、
await を使う関数には必ず async を付けます。


NewsAPI.org の基本的な使い方を押さえる

どんな URL でニュースを取るか

NewsAPI.org にはいくつかエンドポイントがありますが、
1 日目は「すべての記事検索(/everything)」を使います。

ざっくりこんな形です。

https://newsapi.org/v2/everything?q=JavaScript&apiKey=YOUR_API_KEY&language=ja&pageSize=10

q が検索キーワード
apiKey が自分の API キー
language が言語
pageSize が取得件数

JavaScript で URL を組み立てるとこうなります。

const API_KEY = "YOUR_API_KEY"; // 自分のキーに置き換える
const baseUrl = "https://newsapi.org/v2/everything";

function buildUrl(keyword) {
  const params = new URLSearchParams({
    q: keyword,
    apiKey: API_KEY,
    language: "ja",
    pageSize: "10",
    sortBy: "publishedAt"
  });
  return `${baseUrl}?${params.toString()}`;
}
JavaScript

URLSearchParams を使うと、
クエリ文字列(?以降)を安全に組み立てられます。


イベントの準備と「中心となる async 関数」

ボタンから処理を呼び出す

まずは DOM を取ってきます。

const keywordInput = document.getElementById("keywordInput");
const searchButton = document.getElementById("searchButton");
const statusDiv = document.getElementById("status");
const resultDiv = document.getElementById("result");
JavaScript

ボタンにクリックイベントを付けます。

searchButton.addEventListener("click", () => {
  const keyword = keywordInput.value.trim();
  if (!keyword) {
    statusDiv.textContent = "キーワードを入力してください。";
    resultDiv.textContent = "";
    return;
  }

  fetchNews(keyword);
});
JavaScript

ここまでは「きっかけ作り」です。
本番は fetchNews の中身です。


ローディング表示を入れて「ちゃんと待っている」ことを伝える

通信前にローディング状態にする

ユーザーは「押したのに何も起きない」と不安になります。
なので、通信前に必ず「今取りに行ってますよ」と伝えます。

async function fetchNews(keyword) {
  statusDiv.textContent = "ニュースを取得中です…";
  resultDiv.textContent = "";
  searchButton.disabled = true;

  try {
    const url = buildUrl(keyword);
    const response = await fetch(url);

    // ここからレスポンスのチェック
  } catch (error) {
    // 通信レベルのエラー
  } finally {
    // 最後に必ずボタンを戻す
  }
}
JavaScript

ここで重要なのは、
「try の前にローディング表示を出す」
という順番です。


エラーハンドリングの 3 段階を意識する

段階 1:fetch 自体の失敗(ネットワークエラー)

try-catch で拾えるのは、
主に「通信自体が失敗したとき」です。

try {
  const response = await fetch(url);
} catch (error) {
  statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
  console.error(error);
  return;
}
JavaScript

ここで return してしまえば、
この先の処理は実行されません。

段階 2:HTTP ステータスコードのチェック

fetch は、
サーバーが 400 や 500 を返しても
「エラーとして throw しません」。

なので、自分で response.ok をチェックします。

const response = await fetch(url);

if (!response.ok) {
  statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
  return;
}
JavaScript

response.ok は
ステータスコードが 200〜299 のとき true になります。

段階 3:API 独自のエラー(NewsAPI の error)

NewsAPI は、
レスポンスの JSON の中に
status や message を含めてきます。

成功例(ざっくり):

{
  "status": "ok",
  "totalResults": 123,
  "articles": [ ... ]
}

エラー例(ざっくり):

{
  "status": "error",
  "code": "apiKeyInvalid",
  "message": "Your API key is invalid."
}

なので、JSON をパースしたあとに
status をチェックします。

const data = await response.json();

if (data.status === "error") {
  statusDiv.textContent = `エラー:${data.message}`;
  return;
}
JavaScript

成功時のニュース一覧表示を作る

NewsAPI の articles をどう使うか

成功時の JSON には、
articles という配列が入っています。

1 件分はだいたいこんな形です。

{
  "title": "記事タイトル",
  "description": "記事の説明文",
  "url": "https://example.com/article",
  "urlToImage": "https://example.com/image.jpg",
  "publishedAt": "2026-01-27T10:00:00Z",
  "source": { "name": "Example News" }
}

これを使って、
シンプルに「タイトル」「説明」「リンク」を表示します。

function renderArticles(data) {
  const articles = data.articles;

  if (!articles || articles.length === 0) {
    resultDiv.textContent = "該当するニュースが見つかりませんでした。";
    return;
  }

  let html = "";

  articles.forEach((article) => {
    const title = article.title || "タイトルなし";
    const description = article.description || "説明文はありません。";
    const url = article.url;
    const source = article.source?.name || "不明なソース";

    html += `
      <div class="article">
        <h3>${title}</h3>
        <p>${description}</p>
        <p>提供元:${source}</p>
        <p><a href="${url}" target="_blank" rel="noopener noreferrer">記事を読む</a></p>
      </div>
    `;
  });

  resultDiv.innerHTML = html;
}
JavaScript

通信失敗時の分岐を「ユーザー目線」で設計する

失敗パターンをざっくり分ける

1 日目で意識してほしいのは、
「全部まとめて“エラーです”で終わらせない」ことです。

ざっくり、こう分けられます。

ネットワークエラー(オフラインなど)
HTTP エラー(500 番台など)
API キーやリクエストの問題(NewsAPI の status: “error”)

これをユーザーにとって意味のあるメッセージに変えます。

具体的な分岐例をまとめる

async function fetchNews(keyword) {
  statusDiv.textContent = "ニュースを取得中です…";
  resultDiv.textContent = "";
  searchButton.disabled = true;

  try {
    const url = buildUrl(keyword);
    const response = await fetch(url);

    if (!response.ok) {
      statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
      return;
    }

    const data = await response.json();

    if (data.status === "error") {
      statusDiv.textContent = `エラー:${data.message}`;
      return;
    }

    statusDiv.textContent = "ニュースの取得に成功しました。";
    renderArticles(data);

  } catch (error) {
    statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
    console.error(error);

  } finally {
    searchButton.disabled = false;
  }
}
JavaScript

ここでのポイントは、

ローディング表示 → 成功 or 失敗メッセージ
ボタン無効化 → finally で必ず元に戻す

という「始まりと終わりの整え方」です。


1日目の完成コード(重要部分をまとめて)

const API_KEY = "YOUR_API_KEY";
const baseUrl = "https://newsapi.org/v2/everything";

const keywordInput = document.getElementById("keywordInput");
const searchButton = document.getElementById("searchButton");
const statusDiv = document.getElementById("status");
const resultDiv = document.getElementById("result");

function buildUrl(keyword) {
  const params = new URLSearchParams({
    q: keyword,
    apiKey: API_KEY,
    language: "ja",
    pageSize: "10",
    sortBy: "publishedAt"
  });
  return `${baseUrl}?${params.toString()}`;
}

function renderArticles(data) {
  const articles = data.articles;

  if (!articles || articles.length === 0) {
    resultDiv.textContent = "該当するニュースが見つかりませんでした。";
    return;
  }

  let html = "";

  articles.forEach((article) => {
    const title = article.title || "タイトルなし";
    const description = article.description || "説明文はありません。";
    const url = article.url;
    const source = article.source?.name || "不明なソース";

    html += `
      <div class="article">
        <h3>${title}</h3>
        <p>${description}</p>
        <p>提供元:${source}</p>
        <p><a href="${url}" target="_blank" rel="noopener noreferrer">記事を読む</a></p>
      </div>
    `;
  });

  resultDiv.innerHTML = html;
}

async function fetchNews(keyword) {
  statusDiv.textContent = "ニュースを取得中です…";
  resultDiv.textContent = "";
  searchButton.disabled = true;

  try {
    const url = buildUrl(keyword);
    const response = await fetch(url);

    if (!response.ok) {
      statusDiv.textContent = `サーバーエラーが発生しました。(${response.status})`;
      return;
    }

    const data = await response.json();

    if (data.status === "error") {
      statusDiv.textContent = `エラー:${data.message}`;
      return;
    }

    statusDiv.textContent = "ニュースの取得に成功しました。";
    renderArticles(data);

  } catch (error) {
    statusDiv.textContent = "通信に失敗しました。ネットワークを確認してください。";
    console.error(error);

  } finally {
    searchButton.disabled = false;
  }
}

searchButton.addEventListener("click", () => {
  const keyword = keywordInput.value.trim();
  if (!keyword) {
    statusDiv.textContent = "キーワードを入力してください。";
    resultDiv.textContent = "";
    return;
  }
  fetchNews(keyword);
});
JavaScript

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

1 日目で本当に押さえてほしいのは、この 3 つです。

fetch は「Promise を返す非同期処理」である
async/await で「同期っぽく書きつつ、try-catch でエラーを扱う」
エラーハンドリングは「ネットワーク・HTTP・API 独自」の 3 段階で考える

そして、
「ローディング表示 → 成功 or 失敗 → 後片付け」
という一連の流れを、
一つの async 関数の中で組み立てられるようになること。

天気 API でも、ニュース API でも、
この“型”はまったく同じです。

ここまでできたあなたは、
もう「API を叩く練習をしている人」ではなく、
“API 通信の型を身につけ始めた人”です。

2 日目以降は、このニュースアプリに
検索条件の追加(期間・ソース)や
ローディング UI の改善、
エラー表示の整理などを足して、
“使えるニュースアプリ”に育てていきましょう。

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