API からデータを取得する、フォームを非同期で送信する、ファイルをアップロードするなど、fetch API は JavaScript の HTTP 通信の基本です。Promise ベースでコードが読みやすく、async/await と組み合わせることで直感的に書けます。
この記事でわかること
・fetch で GET リクエストを送る基本
・async/await での書き方
・POST リクエスト(JSON / FormData)
・response.ok による HTTP エラーの正しい検出
・try/catch/finally のエラーハンドリング
・AbortController によるリクエストキャンセル・タイムアウト
・レスポンスの型(json / text / blob)
・API データの取得・フォーム送信・ファイルアップロードの実務パターン
・fetch で GET リクエストを送る基本
・async/await での書き方
・POST リクエスト(JSON / FormData)
・response.ok による HTTP エラーの正しい検出
・try/catch/finally のエラーハンドリング
・AbortController によるリクエストキャンセル・タイムアウト
・レスポンスの型(json / text / blob)
・API データの取得・フォーム送信・ファイルアップロードの実務パターン
GET リクエストの基本
then チェーン版
JavaScript
fetch("https://api.example.com/users")
.then(response => {
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error("エラー:", error));
async/await 版(推奨)
JavaScript
async function fetchUsers() {
try {
const response = await fetch("https://api.example.com/users");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error("エラー:", error);
}
}
fetchUsers();
async/await を使うと、非同期処理を同期処理のような見た目で書けます。try/catch でエラーハンドリングもシンプルになるため、新しいコードでは async/await が推奨です。
response.ok による HTTP エラーの検出
fetch はネットワークエラー以外では reject しません。404 や 500 などの HTTP エラーでも then に進みます。response.ok(ステータス 200〜299)を必ずチェックしてください。
| 状況 | fetch の動作 | catch で捕捉 |
|---|---|---|
| ステータス 200 | resolve(正常) | — |
| ステータス 404 | resolve(response.ok === false) |
× 捕捉されない |
| ステータス 500 | resolve(response.ok === false) |
× 捕捉されない |
| ネットワークエラー | reject | ○ 捕捉される |
| CORS エラー | reject | ○ 捕捉される |
fetch の catch はネットワークエラーのみ捕捉します。HTTP 404 / 500 は catch に入らないため、
if (!response.ok) throw new Error() で手動でエラーにする必要があります。これは fetch の最大の落とし穴です。POST リクエスト
JSON を送信する
JavaScript
async function createUser(user) {
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
}
const newUser = await createUser({ name: "田中", email: "tanaka@example.com" });
console.log(newUser);
FormData を送信する
JavaScript
const form = document.getElementById("myForm");
form.addEventListener("submit", async (e) => {
e.preventDefault();
const response = await fetch("/api/submit", {
method: "POST",
body: new FormData(form) // Content-Type は自動設定される
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
console.log(result);
});
FormData を body に渡す場合、
Content-Type ヘッダーは設定しないでください。ブラウザが自動的に multipart/form-data と適切な boundary を設定します。手動で設定するとエラーになります。レスポンスの型(json / text / blob)
| メソッド | 戻り値 | 用途 |
|---|---|---|
response.json() |
パースされた JavaScript オブジェクト | JSON API のレスポンス |
response.text() |
文字列 | HTML / XML / プレーンテキスト |
response.blob() |
Blob オブジェクト | 画像 / ファイルのダウンロード |
response.arrayBuffer() |
ArrayBuffer | バイナリデータ |
response.formData() |
FormData | multipart レスポンス |
レスポンスの body は1 回しか読めません。
response.json() を呼んだ後に response.text() を呼ぶとエラーになります。デバッグ用にテキストも見たい場合は response.clone().text() を使ってください。fetch のオプション一覧
| オプション | 説明 | 例 |
|---|---|---|
method |
HTTP メソッド | "GET" / "POST" / "PUT" / "DELETE" |
headers |
リクエストヘッダー | { "Content-Type": "application/json" } |
body |
リクエストボディ | JSON.stringify(data) / new FormData(form) |
credentials |
Cookie の送信 | "same-origin"(デフォルト)/ "include"(クロスオリジン) |
signal |
AbortController の signal | リクエストキャンセル用 |
mode |
CORS モード | "cors"(デフォルト)/ "no-cors" / "same-origin" |
AbortController でリクエストをキャンセルする
JavaScript
const controller = new AbortController();
async function fetchData() {
try {
const response = await fetch("/api/data", {
signal: controller.signal
});
const data = await response.json();
console.log(data);
} catch (error) {
if (error.name === "AbortError") {
console.log("リクエストがキャンセルされました");
} else {
console.error("エラー:", error);
}
}
}
fetchData();
// 必要なタイミングでキャンセル
controller.abort();
タイムアウトを実装する
JavaScript
async function fetchWithTimeout(url, options = {}, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
return response;
} finally {
clearTimeout(timeoutId);
}
}
// 使用例: 5秒でタイムアウト
const res = await fetchWithTimeout("/api/slow-endpoint", {}, 5000);
fetch にはタイムアウト機能が組み込まれていないため、AbortController + setTimeout で自作します。サーバーの応答が遅い場合のフォールバックとして必須です。
実務でよく使うパターン
API データを取得して DOM に表示する
JavaScript
async function renderUsers() {
const list = document.getElementById("userList");
list.textContent = "読み込み中...";
try {
const res = await fetch("/api/users");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const users = await res.json();
list.innerHTML = users
.map(u => `<li>${u.name} (${u.email})</li>`)
.join("");
} catch (error) {
list.textContent = "データの取得に失敗しました";
console.error(error);
}
}
renderUsers();
汎用 API クライアント関数
JavaScript
async function api(endpoint, options = {}) {
const defaultHeaders = { "Content-Type": "application/json" };
const response = await fetch(endpoint, {
headers: { ...defaultHeaders, ...options.headers },
...options,
body: options.body ? JSON.stringify(options.body) : undefined
});
if (!response.ok) {
const error = await response.text();
throw new Error(`HTTP ${response.status}: ${error}`);
}
return response.json();
}
// 使用例
const users = await api("/api/users");
const newUser = await api("/api/users", {
method: "POST",
body: { name: "田中", email: "tanaka@example.com" }
});
リトライ付き fetch
JavaScript
async function fetchWithRetry(url, options = {}, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url, options);
if (res.ok) return res;
if (res.status < 500) throw new Error(`HTTP ${res.status}`); // 4xx はリトライしない
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1))); // 指数バックオフ
}
}
}
関連記事
- setTimeout の使い方 — デバウンス・Promise 化
- 送信ボタンの二重クリック防止
- イベントを無効化する方法 — AbortController
- ファイルをダウンロードする方法 — fetch + Blob
- クリックイベントの設定方法
よくある質問
Qfetch で 404 エラーが catch されません。
Afetch は HTTP エラー(404 / 500)では reject しません。
response.ok をチェックして、false の場合は手動で throw new Error() してください。catch が捕捉するのはネットワークエラーと CORS エラーのみです。Qfetch と axios の違いは何ですか?
Afetch はブラウザ組み込みで追加インストール不要。axios はライブラリで、自動 JSON 変換・HTTP エラーの自動 reject・リクエストインターセプターなどの機能があります。シンプルな通信なら fetch で十分、複雑な API クライアントなら axios が便利です。
QFormData を送信するときに Content-Type を設定してはいけないのはなぜですか?
AFormData を body に渡すと、ブラウザが
multipart/form-data と適切な boundary を自動設定します。手動で Content-Type を設定すると boundary が含まれず、サーバーがリクエストを正しくパースできなくなります。Qfetch にタイムアウト機能はありますか?
A組み込みのタイムアウト機能はありません。
AbortController + setTimeout で自作します。setTimeout(() => controller.abort(), 5000) のように設定してください。Qfetch で Cookie を送信するにはどうすればよいですか?
A同一オリジンではデフォルトで Cookie が送信されます。クロスオリジンで Cookie を送信するには
credentials: "include" を設定し、サーバー側でも Access-Control-Allow-Credentials: true を返す必要があります。まとめ
fetch API の使い方を整理しました。
- 基本:
await fetch(url)+response.json() - HTTP エラー検出:
if (!response.ok) throw new Error()(最重要!) - POST:
method: "POST"+body: JSON.stringify(data) - FormData:
body: new FormData(form)(Content-Type 設定不要) - キャンセル:
AbortControllerのsignalオプション - タイムアウト: AbortController + setTimeout で自作
response.ok のチェックを忘れると、404 や 500 エラーが見逃されるという落とし穴があります。fetch を使う際は必ず response.ok を確認し、エラー時には適切な UI フィードバック(エラーメッセージの表示など)を実装しましょう。