本文へスキップ
JavaScript 応用 JavaScript fetch API

fetchを使ってAPIからデータを取得しよう

更新日:2026-08-02

fetch()を使うと、ページを移動せずにAPIからデータを取得できます。通信には失敗する可能性があるため、状態ごとの表示も用意します。

APIは、プログラム同士がデータや機能をやり取りするための窓口です。今回は自作のJSONファイルで基本を確認した後、学習用の外部APIであるJSONPlaceholderから投稿データを取得します。

今回使用するファイル構成

javascript-practice/
├── index.html
├── data/
│   └── articles.json
└── js/
    └── script.js

APIからJSONを取得して表示する

練習用JSONを用意する

data/articles.jsonを作成します。

[
  {
    "id": 1,
    "title": "JavaScriptの歴史",
    "category": "入門"
  },
  {
    "id": 2,
    "title": "DOM操作を学ぼう",
    "category": "基礎"
  }
]

JSONはデータ交換でよく使われる文字列形式です。プロパティ名と文字列をダブルクォートで囲み、最後の項目の後ろにはカンマを付けません。

表示先を用意する

<button id="load-button" type="button">記事を取得する</button>
<p id="status" aria-live="polite"></p>
<ul id="article-list"></ul>

aria-live="polite"を指定すると、通信状態の変化を支援技術へ伝えられます。

fetchでJSONを取得する

const loadButton = document.querySelector('#load-button');
const status = document.querySelector('#status');
const articleList = document.querySelector('#article-list');

async function loadArticles() {
  status.textContent = '読み込み中です';
  loadButton.disabled = true;

  try {
    const response = await fetch('data/articles.json');
    const articles = await response.json();

    articleList.replaceChildren();
    articles.forEach((article) => {
      const item = document.createElement('li');
      item.textContent = article.title;
      articleList.append(item);
    });

    status.textContent = '読み込みが完了しました';
  } catch (error) {
    status.textContent = 'データを取得できませんでした';
    console.error(error);
  } finally {
    loadButton.disabled = false;
  }
}

loadButton.addEventListener('click', loadArticles);

JSONPlaceholderからデータを取得する

自作のJSONでは、ファイルの内容とURLを自分で管理できます。次は、自分のサイトとは異なるサーバーが公開しているAPIからデータを取得してみましょう。

JSONPlaceholderとは

JSONPlaceholderは、API通信の学習や動作確認に使える無料のフェイクREST APIです。投稿、コメント、ユーザー、Todoなどのサンプルデータが用意されており、自分でAPIサーバーやデータベースを作る前でもfetch()を練習できます。

REST APIは、HTTPのURLとメソッドを使ってデータを操作する設計方法の一つです。今回はデータを取得するため、HTTPのGETメソッドを使います。fetch()は、メソッドを省略するとGETでリクエストします。

JSONPlaceholderのデータは学習用です。実在する投稿や利用者の情報ではありません。また、作成・更新・削除のリクエストも試せますが、公式ガイドに記載されているとおり、サーバーへ実際に永続保存されるわけではありません。

エンドポイントをブラウザで確認する

APIでデータを取得するURLをエンドポイントと呼びます。今回は、ユーザーIDが1の投稿を返す次のエンドポイントを使います。

https://jsonplaceholder.typicode.com/posts?userId=1

URLをブラウザのアドレス欄へ貼り付けると、JSON形式の配列が表示されます。一件分のデータは次の構造です。

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit..."
}
  • userId:投稿者を表すID
  • id:投稿を表すID
  • title:投稿タイトル
  • body:投稿本文

URLの?userId=1はクエリパラメータです。この条件によって、ユーザーIDが1の投稿だけを取得します。

外部API用の表示ボタンを追加する

index.htmlの自作JSON取得ボタンの後ろへ、ボタンを一つ追加します。表示先の#status#article-listは共通で使います。

<button id="load-button" type="button">自作JSONを取得する</button>
<button id="load-placeholder-button" type="button">
  JSONPlaceholderから取得する
</button>
<p id="status" aria-live="polite"></p>
<ul id="article-list"></ul>

JSONPlaceholderへfetchする

js/script.jsへ、外部APIから投稿を取得する関数を追加します。

const placeholderButton = document.querySelector(
  '#load-placeholder-button'
);

async function loadPlaceholderPosts() {
  status.textContent = 'JSONPlaceholderから読み込んでいます';
  placeholderButton.disabled = true;

  try {
    const response = await fetch(
      'https://jsonplaceholder.typicode.com/posts?userId=1'
    );
    const posts = await response.json();

    articleList.replaceChildren();

    posts.forEach((post) => {
      const item = document.createElement('li');
      const heading = document.createElement('h2');
      const body = document.createElement('p');

      heading.textContent = post.title;
      body.textContent = post.body;
      item.append(heading, body);
      articleList.append(item);
    });

    status.textContent = `${posts.length}件の投稿を読み込みました`;
  } catch (error) {
    status.textContent = 'JSONPlaceholderから取得できませんでした';
    console.error(error);
  } finally {
    placeholderButton.disabled = false;
  }
}

placeholderButton.addEventListener('click', loadPlaceholderPosts);

自作JSONの例ではarticle.titleを使いましたが、JSONPlaceholderでは一件分の変数をpostとし、post.titlepost.bodyを表示しています。APIごとにデータ構造が異なるため、取得前に仕様を確認し、実際のJSONもConsoleやNetworkタブで確認することが大切です。

自作JSONとの違いを確認する

二つのボタンを交互に押し、Networkタブでリクエストを比較します。

  • 自作JSONは自分のLive Serverと同じオリジンから取得する
  • JSONPlaceholderはjsonplaceholder.typicode.comという別のオリジンから取得する
  • 外部APIの利用にはインターネット接続が必要になる
  • 外部APIのURL、データ構造、利用条件は提供者によって決められる
  • 別オリジンへの通信は、API側がCORSで許可している必要がある

CORSは、あるオリジンのWebページから別のオリジンへ通信してよいかをブラウザが確認する仕組みです。JSONPlaceholderはブラウザからの学習用途で取得できますが、すべての外部URLを同じようにfetch()できるわけではありません。

実際のサービスで外部APIを使用するときは、利用規約、認証方法、回数制限、障害時の対応も確認します。APIキーなどの秘密情報をフロント側のJavaScriptへ直接書いてはいけません。

JSONPlaceholderの通信を確認する

  1. 「JSONPlaceholderから取得する」を押す
  2. 読み込み中の文章とボタンの無効化を確認する
  3. 投稿タイトルと本文が一覧表示されることを確認する
  4. 完了後に取得件数が表示されることを確認する
  5. NetworkタブでリクエストURL、Status、Responseを確認する

JSONPlaceholderの投稿は英語のダミーテキストです。文字化けや翻訳の失敗ではありません。

HTTPエラー処理への準備

fetch()は404や500でもレスポンスを受け取るため、必ずしもcatchへ移りません。次の記事ではresponse.okを確認し、通信エラーを適切に扱います。

fetchの処理を一段ずつ理解する

fetchが返すもの

const responsePromise = fetch('data/articles.json');
console.log(responsePromise);

fetch()はデータそのものではなくPromiseを返します。awaitで待つとResponseオブジェクトを取得できます。

const response = await fetch('data/articles.json');
console.log(response.status);
console.log(response.headers.get('content-type'));

ResponseにはHTTPステータス、レスポンスヘッダー、本文などの情報があります。

response.jsonも非同期処理である

const articles = await response.json();

response.json()は本文を読み取り、JSONをJavaScriptの配列やオブジェクトへ変換します。この処理もPromiseを返すためawaitが必要です。一つのレスポンス本文は原則として一度だけ読み取ります。

取得したデータを検査する

外部から得たデータが常に想定どおりとは限りません。最低限、配列かどうかを確認します。

if (!Array.isArray(articles)) {
  throw new Error('記事データが配列ではありません');
}

Live Serverで動作確認する

HTMLファイルをダブルクリックしてfile://で開くと、ブラウザのセキュリティ制限でfetch()が失敗する場合があります。VS CodeのLive Serverなどを使い、http://から始まるURLで確認してください。

DevToolsのNetworkタブでは、次の項目を確認できます。

  1. articles.jsonのリクエストがある
  2. Statusが200である
  3. ResponseにJSONが表示される
  4. ボタンを押してから完了するまでの時間が記録される

よくあるエラー

Unexpected tokenと表示される

JSONのカンマ、ダブルクォート、波括弧を確認します。NetworkタブのResponseがHTMLの場合は、URLの間違いによって404ページを取得している可能性があります。

Failed to fetchと表示される

Live Serverで開いているか、URLが同じオリジンか、サーバーが起動しているかを確認します。

ボタンを連打すると一覧が何度も更新される

通信開始時にdisabled = truefinallyfalseへ戻します。通信中の状態を画面にも表示してください。

まとめ

通信中、成功、失敗の状態を画面へ表示し、連続操作を防ぐことも実用上重要です。次の記事ではHTTPエラーと例外処理を詳しく扱います。