【初心者向け】JavaScript fetchとは?APIデータ取得の基礎

JavaScript fetch APIについて初心者向けに完全解説。基本的なデータ取得からエラーハンドリングまで、実例を交えて詳しく説明します。

Learning Next 運営
37 分で読めます

【初心者向け】JavaScript fetchとは?APIデータ取得の基礎

Webページでサーバーからデータを取得したいと思ったことはありませんか?

「API からデータを取得したいけれど、どうやるの?」 「fetch って何?どうやって使うの?」

そんな疑問を抱えている方も多いと思います。 でも大丈夫です!

この記事では、JavaScript fetch API について初心者向けに詳しく解説します。 基本的なデータ取得からエラーハンドリングまで、実例を交えてfetchの使い方をマスターしましょう。

きっと「こんなに簡単だったんだ!」と感じられるはずですよ。

fetch APIって何?現代的なデータ通信の仕組み

基本的な概念を理解しよう

fetch APIは、サーバーからデータを取得したり、サーバーにデータを送信したりするためのJavaScriptの現代的な機能です。

簡単に言うと、「Webページを再読み込みせずに、サーバーと通信できる」技術です。 これにより、天気情報の取得、ユーザー情報の更新、商品データの読み込みなどができます。

イメージとしては、お店に電話で商品の在庫を確認するような感じですね。 わざわざお店に行かなくても、必要な情報だけを取得できます。

fetchの素晴らしい特徴

fetch APIの主な特徴を確認してみましょう。

  • Promise ベース:非同期処理を直感的に記述
  • モダンな構文:async/awaitと組み合わせて使いやすい
  • 柔軟なリクエスト設定:ヘッダーやメソッドを自由に設定
  • 標準機能:外部ライブラリ不要
  • ストリーミング対応:大きなファイルも効率的に処理

従来のXMLHttpRequestより使いやすく、現代的なWeb開発の標準となっています。 まさに、今の時代にぴったりのツールですね。

基本的な使い方をマスターしよう

最もシンプルなGETリクエスト

fetchの最も基本的な使用方法から始めましょう。

// 基本的なfetch(GETリクエスト)
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => {
console.log('取得したデータ:', data);
})
.catch(error => {
console.error('エラーが発生しました:', error);
});

この例では、指定したURLからJSONデータを取得して表示しています。 fetch() でデータを取得し、.then() で処理を続けています。

.json() メソッドでレスポンスをJSONに変換するのがポイントです。 エラーが発生した場合は .catch() でキャッチします。

async/awaitを使った分かりやすい書き方

より読みやすいasync/await構文を使用した例を見てみましょう。

async function fetchUserData() {
try {
let response = await fetch('https://api.example.com/users');
let data = await response.json();
console.log('取得したデータ:', data);
return data;
} catch (error) {
console.error('エラーが発生しました:', error);
return null;
}
}
// 関数を呼び出し
fetchUserData();

async/await を使うことで、同期的なコードのように書けます。 await で非同期処理の完了を待ってから次の処理に進みます。

try/catch 文でエラーハンドリングも分かりやすくなりますね。

レスポンスのステータス確認

HTTPステータスコードを確認する重要な処理を学びましょう。

async function fetchWithStatusCheck(url) {
try {
let response = await fetch(url);
// ステータスコードの確認
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let data = await response.json();
return data;
} catch (error) {
console.error('取得エラー:', error.message);
return null;
}
}
// 使用例
fetchWithStatusCheck('https://api.example.com/users/123');

response.ok でステータスコードが200番台かどうかを確認しています。 問題がある場合は throw new Error() でエラーを投げます。

これにより、サーバーエラーも適切に処理できるようになります。

実践的なデータ取得例で理解を深めよう

ユーザー情報の取得と表示

実際のWebページでユーザー情報を取得・表示する例を見てみましょう。

async function displayUserInfo(userId) {
// ローディング表示
showLoading(true);
try {
let response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!response.ok) {
throw new Error('ユーザーが見つかりません');
}
let user = await response.json();
// ユーザー情報を画面に表示
updateUserDisplay(user);
} catch (error) {
showError(error.message);
} finally {
showLoading(false);
}
}

この関数では、まずローディング表示を開始しています。 データ取得後、成功すれば表示を更新し、失敗すればエラーを表示します。

finally ブロックで必ずローディングを非表示にするのがポイントです。

ここで使用している関数の詳細も見てみましょう。

function updateUserDisplay(user) {
let userInfoElement = document.getElementById('userInfo');
userInfoElement.innerHTML = `
<h3>${user.name}</h3>
<p>メール: ${user.email}</p>
<p>電話: ${user.phone}</p>
<p>ウェブサイト: ${user.website}</p>
`;
}
function showLoading(show) {
let loader = document.getElementById('loader');
loader.style.display = show ? 'block' : 'none';
}
function showError(message) {
let errorElement = document.getElementById('error');
errorElement.textContent = message;
errorElement.style.display = 'block';
}
// 使用例
displayUserInfo(1);

updateUserDisplay() では、取得したユーザー情報をHTMLに変換しています。 showLoading()showError() でユーザーへの状態表示を管理します。

これにより、ユーザビリティの高いアプリケーションが作れますね。

商品リストの取得と表示

商品データを取得してリスト表示する実用的な例です。

async function loadProductList() {
try {
let response = await fetch('https://fakestoreapi.com/products');
let products = await response.json();
displayProducts(products);
} catch (error) {
console.error('商品データの取得に失敗:', error);
showErrorMessage('商品データを読み込めませんでした');
}
}

まず商品データを取得し、成功すれば表示関数を呼び出します。 エラーが発生した場合は、ユーザーに分かりやすいメッセージを表示します。

商品表示の処理も見てみましょう。

function displayProducts(products) {
let productContainer = document.getElementById('productList');
productContainer.innerHTML = '';
products.forEach(product => {
let productElement = createProductElement(product);
productContainer.appendChild(productElement);
});
}
function createProductElement(product) {
let div = document.createElement('div');
div.className = 'product-item';
div.innerHTML = `
<img src="${product.image}" alt="${product.title}" width="100">
<h4>${product.title}</h4>
<p>価格: $${product.price}</p>
<p>${product.description.substring(0, 100)}...</p>
`;
return div;
}
// ページ読み込み時に実行
document.addEventListener('DOMContentLoaded', loadProductList);

displayProducts() では、商品コンテナをクリアしてから新しい商品を追加します。 createProductElement() で各商品のHTML要素を作成します。

説明文を100文字に制限しているのが実用的なポイントですね。

POSTリクエストでデータ送信をマスターしよう

基本的なPOSTリクエスト

サーバーにデータを送信する基本的な方法を学びましょう。

async function createUser(userData) {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let result = await response.json();
console.log('ユーザー作成成功:', result);
return result;
} catch (error) {
console.error('ユーザー作成エラー:', error);
return null;
}
}

POSTリクエストでは、method: 'POST' を指定します。 headers でContent-Typeを設定し、body でデータを送信します。

JSON.stringify() でJavaScriptオブジェクトをJSON文字列に変換するのがポイントです。

使用例も確認してみましょう。

// 使用例
let newUser = {
name: '田中太郎',
email: 'tanaka@example.com',
username: 'tanaka123'
};
createUser(newUser);

このように、オブジェクト形式でデータを準備してから送信します。 サーバー側で適切に処理されれば、新しいユーザーが作成されますね。

フォームデータの送信

HTMLフォームからのデータ送信例を見てみましょう。

async function submitForm(event) {
event.preventDefault(); // デフォルトのフォーム送信を防ぐ
let form = event.target;
let formData = new FormData(form);
// FormDataをJSONオブジェクトに変換
let jsonData = {};
for (let [key, value] of formData.entries()) {
jsonData[key] = value;
}
try {
let response = await fetch('/api/contact', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(jsonData)
});
if (response.ok) {
let result = await response.json();
showSuccessMessage('送信が完了しました');
form.reset(); // フォームをリセット
} else {
throw new Error('送信に失敗しました');
}
} catch (error) {
showErrorMessage(error.message);
}
}
// フォームにイベントリスナーを追加
document.getElementById('contactForm').addEventListener('submit', submitForm);

event.preventDefault() でブラウザのデフォルト送信を防ぎます。 FormData を使ってフォームデータを取得し、JSONに変換します。

送信成功時は form.reset() でフォームをクリアするのも親切ですね。

ファイルアップロード機能

ファイルをサーバーにアップロードする例も見てみましょう。

async function uploadFile(fileInput) {
let file = fileInput.files[0];
if (!file) {
alert('ファイルを選択してください');
return;
}
let formData = new FormData();
formData.append('file', file);
formData.append('description', 'アップロードファイル');
try {
showProgress(0);
let response = await fetch('/api/upload', {
method: 'POST',
body: formData // Content-Typeヘッダーは自動設定される
});
if (response.ok) {
let result = await response.json();
showSuccessMessage(`ファイルがアップロードされました: ${result.filename}`);
} else {
throw new Error('アップロードに失敗しました');
}
} catch (error) {
showErrorMessage(error.message);
} finally {
hideProgress();
}
}

ファイルアップロードでは FormData を使用します。 formData.append() でファイルや追加情報を設定します。

ファイルアップロード時は、Content-Typeヘッダーを指定しません。 ブラウザが自動的に適切な値を設定してくれます。

プログレス表示の関数も用意しておきましょう。

function showProgress(percent) {
let progressBar = document.getElementById('progressBar');
progressBar.style.width = percent + '%';
progressBar.style.display = 'block';
}
function hideProgress() {
let progressBar = document.getElementById('progressBar');
progressBar.style.display = 'none';
}

これにより、ユーザーにアップロード状況を視覚的に伝えることができます。

エラーハンドリングで堅牢なアプリを作ろう

包括的なエラー処理

様々なエラーケースに対応する処理を実装してみましょう。

async function robustFetch(url, options = {}) {
try {
let response = await fetch(url, options);
// ネットワークエラーではない場合のHTTPエラー
if (!response.ok) {
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
// エラーレスポンスの詳細を取得(可能な場合)
try {
let errorData = await response.json();
if (errorData.message) {
errorMessage += ` - ${errorData.message}`;
}
} catch (parseError) {
// JSONパースに失敗した場合は無視
}
throw new Error(errorMessage);
}
return response;
} catch (error) {
// ネットワークエラーまたはその他のエラー
if (error.name === 'TypeError' && error.message.includes('fetch')) {
throw new Error('ネットワーク接続エラーが発生しました');
}
throw error; // その他のエラーはそのまま再投げ
}
}

この関数では、HTTPエラーとネットワークエラーを区別して処理しています。 エラーレスポンスに詳細情報があれば、それも含めてエラーメッセージを作成します。

ネットワークエラーの場合は、分かりやすいメッセージに変換します。

使用例も確認してみましょう。

// 使用例
async function getUserData(userId) {
try {
let response = await robustFetch(`/api/users/${userId}`);
let userData = await response.json();
return userData;
} catch (error) {
console.error('ユーザーデータ取得エラー:', error.message);
return null;
}
}

このように、robust な fetch 関数を使うことで、エラー処理が統一されます。 アプリケーション全体で一貫したエラーハンドリングができますね。

リトライ機能付きfetch

失敗時に自動的にリトライする便利な機能を実装してみましょう。

async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
let response = await fetch(url, options);
if (response.ok) {
return response;
}
// 4xx系のエラーはリトライしない
if (response.status >= 400 && response.status < 500) {
throw new Error(`クライアントエラー: ${response.status}`);
}
// 5xx系のエラーはリトライする
lastError = new Error(`サーバーエラー: ${response.status}`);
} catch (error) {
lastError = error;
// ネットワークエラー以外はリトライしない
if (!error.message.includes('fetch')) {
throw error;
}
}
// 最後の試行でない場合は待機
if (attempt < maxRetries) {
let delay = Math.pow(2, attempt) * 1000; // 指数バックオフ
console.log(`${attempt}回目の試行が失敗。${delay}ms後にリトライします...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error(`${maxRetries}回のリトライ後も失敗: ${lastError.message}`);
}

この関数では、リトライすべきエラーとそうでないエラーを区別しています。 4xx系のクライアントエラーはリトライせず、5xx系のサーバーエラーはリトライします。

指数バックオフで待機時間を徐々に伸ばしているのもポイントです。

使用例も確認してみましょう。

// 使用例
async function reliableDataFetch(url) {
try {
let response = await fetchWithRetry(url);
let data = await response.json();
return data;
} catch (error) {
console.error('データ取得に完全に失敗:', error.message);
return null;
}
}

このように、ネットワークが不安定な環境でも安定してデータ取得ができます。

高度なfetch活用法をマスターしよう

リクエストのキャンセル機能

AbortControllerを使用してリクエストをキャンセルする方法を学びましょう。

class DataLoader {
constructor() {
this.currentController = null;
}
async loadData(url) {
// 前のリクエストをキャンセル
if (this.currentController) {
this.currentController.abort();
}
// 新しいAbortControllerを作成
this.currentController = new AbortController();
try {
let response = await fetch(url, {
signal: this.currentController.signal
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let data = await response.json();
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('リクエストがキャンセルされました');
return null;
}
throw error;
} finally {
this.currentController = null;
}
}
cancelCurrentRequest() {
if (this.currentController) {
this.currentController.abort();
}
}
}

AbortController を使うことで、進行中のリクエストをキャンセルできます。 signal オプションにコントローラーのシグナルを渡します。

キャンセル時は AbortError が発生するので、それを適切に処理します。

使用例も見てみましょう。

// 使用例
let loader = new DataLoader();
// データ読み込み
loader.loadData('/api/large-dataset');
// 必要に応じてキャンセル
setTimeout(() => {
loader.cancelCurrentRequest();
}, 5000); // 5秒後にキャンセル

長時間かかるリクエストや、ユーザーが画面を切り替えた場合などに便利ですね。

プログレス表示付きダウンロード

大きなファイルのダウンロード進捗を表示する高度な例を見てみましょう。

async function downloadWithProgress(url, onProgress) {
try {
let response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let contentLength = response.headers.get('content-length');
let total = parseInt(contentLength, 10);
let loaded = 0;
let reader = response.body.getReader();
let chunks = [];
while (true) {
let { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
// プログレスコールバックを呼び出し
if (onProgress && total > 0) {
let progress = (loaded / total) * 100;
onProgress(progress, loaded, total);
}
}
// 全チャンクを結合
let allChunks = new Uint8Array(loaded);
let position = 0;
for (let chunk of chunks) {
allChunks.set(chunk, position);
position += chunk.length;
}
return allChunks;
} catch (error) {
console.error('ダウンロードエラー:', error);
throw error;
}
}

この関数では、レスポンスのストリームを読み取って進捗を追跡しています。 response.body.getReader() でリーダーを取得し、チャンクごとに読み込みます。

各チャンクのサイズから進捗率を計算し、コールバック関数で通知します。

プログレス表示の実装も確認してみましょう。

// 使用例
function updateProgressBar(progress, loaded, total) {
let progressBar = document.getElementById('progressBar');
let progressText = document.getElementById('progressText');
progressBar.style.width = progress + '%';
progressText.textContent = `${Math.round(progress)}% (${loaded} / ${total} bytes)`;
}
// ダウンロード実行
downloadWithProgress('/api/large-file.zip', updateProgressBar)
.then(data => {
console.log('ダウンロード完了:', data.length, 'bytes');
})
.catch(error => {
console.error('ダウンロード失敗:', error);
});

プログレスバーとテキストで進捗を視覚的に表示します。 ユーザーが待機時間を把握できるので、ユーザビリティが向上しますね。

実用的なAPIクライアントクラスを作ろう

汎用APIクライアントの実装

再利用可能なAPIクライアントクラスを実装してみましょう。

class APIClient {
constructor(baseURL, defaultHeaders = {}) {
this.baseURL = baseURL;
this.defaultHeaders = {
'Content-Type': 'application/json',
...defaultHeaders
};
}
async request(endpoint, options = {}) {
let url = this.baseURL + endpoint;
let config = {
headers: { ...this.defaultHeaders, ...options.headers },
...options
};
try {
let response = await fetch(url, config);
if (!response.ok) {
let errorData = null;
try {
errorData = await response.json();
} catch (e) {
// JSONパースに失敗した場合は無視
}
throw new APIError(
response.status,
response.statusText,
errorData
);
}
return await response.json();
} catch (error) {
if (error instanceof APIError) {
throw error;
}
throw new APIError(0, 'Network Error', { message: error.message });
}
}
get(endpoint, params = {}) {
let url = endpoint;
if (Object.keys(params).length > 0) {
let searchParams = new URLSearchParams(params);
url += '?' + searchParams.toString();
}
return this.request(url, { method: 'GET' });
}
post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
delete(endpoint) {
return this.request(endpoint, { method: 'DELETE' });
}
}

このクラスでは、基本的なHTTPメソッドのラッパーを提供しています。 ベースURLとデフォルトヘッダーを設定することで、統一的にAPIを呼び出せます。

エラー処理も専用のクラスで管理しています。

カスタムエラークラスも実装しましょう。

class APIError extends Error {
constructor(status, statusText, data) {
super(`API Error: ${status} ${statusText}`);
this.status = status;
this.statusText = statusText;
this.data = data;
}
}

このエラークラスにより、APIエラーの詳細情報を管理できます。 ステータスコードやレスポンスデータも含まれるので、デバッグに便利ですね。

使用例も確認してみましょう。

// 使用例
let api = new APIClient('https://api.example.com', {
'Authorization': 'Bearer your-token-here'
});
// ユーザー一覧取得
async function getUsers() {
try {
let users = await api.get('/users', { page: 1, limit: 10 });
console.log('ユーザー一覧:', users);
return users;
} catch (error) {
if (error instanceof APIError) {
console.error(`API エラー [${error.status}]:`, error.data);
} else {
console.error('予期しないエラー:', error);
}
}
}
// 新規ユーザー作成
async function createUser(userData) {
try {
let newUser = await api.post('/users', userData);
console.log('ユーザー作成成功:', newUser);
return newUser;
} catch (error) {
console.error('ユーザー作成エラー:', error);
}
}

このAPIクライアントを使うことで、一貫したAPI呼び出しができます。 認証トークンの管理やエラーハンドリングも統一されますね。

まとめ:fetch APIをマスターして次のステップへ

JavaScript fetch API は、現代的なWeb開発において必須の技術です。

今回学習した内容をおさらいしましょう。

基本的な使い方から始まり、実践的な応用まで幅広くカバーしました。

  • fetchはPromiseベースの現代的なHTTP通信API
  • async/awaitと組み合わせることで読みやすいコードを記述
  • GET、POST、PUT、DELETEなど様々なHTTPメソッドに対応
  • エラーハンドリングとリトライ機能で堅牢な通信を実現
  • AbortControllerによるリクエストキャンセル機能
  • プログレス表示など高度な機能も実装可能

実践のポイントも忘れずに。

  • 適切なエラーハンドリングでユーザビリティを向上
  • ローディング表示でユーザーに状況を伝える
  • リトライ機能で不安定なネットワークに対応
  • APIクライアントクラスで統一的な処理を実現

fetchを適切に使用することで、ユーザーフレンドリーで信頼性の高いWebアプリケーションを作成できます。 特に、エラーハンドリングとユーザビリティの配慮は、実際のプロジェクトでは欠かせません。

これらの基礎技術をマスターして、データドリブンなWebアプリケーションを作成してみませんか? きっと素晴らしいアプリケーションが作れるはずです!

関連記事