axios vs ofetch — HTTP クライアント徹底比較【2025年版】
1. 結論
新規プロジェクトや Nuxt/Nitro エコシステムで開発するなら ofetch が軽量かつモダンな選択肢です。既存の大規模プロジェクトや、インターセプター・進捗監視など豊富な機能が必要なら axios が依然として堅実です。どちらも本番運用に十分な品質を持っており、プロジェクトの要件に合わせて選ぶのが最善です。
2. 比較表
| 観点 | axios | ofetch |
|---|---|---|
| GitHub Stars | ≈ 106k | ≈ 4.5k |
| npm 週間DL数 | ≈ 5,500万 | ≈ 1,000万 |
| バンドルサイズ (minified+gzip) | ≈ 13 kB | ≈ 3 kB |
| TypeScript 対応 | ✅ 同梱型定義 | ✅ ネイティブ TS で記述 |
| ランタイム対応 | Node.js / ブラウザ | Node.js / ブラウザ / Workers / Deno / Bun |
| ベース API | XMLHttpRequest (ブラウザ) / http (Node) | Fetch API (全環境統一) |
| JSON 自動パース | ✅ | ✅ |
| リクエスト/レスポンス インターセプター | ✅ 組み込み | ⚠️ onRequest / onResponse フックで実現 |
| リトライ機構 | ❌ 別途プラグイン必要 | ✅ 組み込み(デフォルト1回) |
| アップロード進捗 | ✅ onUploadProgress | ❌ Fetch API の制約上なし |
| リクエストキャンセル | ✅ AbortController / 旧 CancelToken | ✅ AbortController(Fetch 標準) |
| baseURL 設定 | ✅ | ✅ (create で設定) |
| 自動エラーハンドリング | ✅ ステータスコードで reject | ✅ FetchError をスロー |
| 学習コスト | 低(情報量が圧倒的に多い) | 低〜中(Fetch 経験者なら容易) |
| メンテナー | コミュニティ | UnJS (Nuxt コアチーム) |
3. それぞれの強み
axios の強み
-
圧倒的な情報量とエコシステム Stack Overflow の回答数、ブログ記事、サードパーティプラグイン(
axios-retry、axios-mock-adapterなど)が非常に豊富です。チームに新しいメンバーが入っても学習リソースに困りません。 -
インターセプターの柔軟性 リクエスト/レスポンスの前後処理をチェーンで登録でき、認証トークンの付与やグローバルエラーハンドリングを宣言的に記述できます。
-
アップロード/ダウンロード進捗の監視
onUploadProgress/onDownloadProgressコールバックが組み込みで提供されており、ファイルアップロード UI の実装が容易です。 -
ブラウザ互換性 内部で
XMLHttpRequestを使用するため、Fetch API 未対応の古い環境でもポリフィルなしで動作します。
ofetch の強み
-
圧倒的な軽量さ gzip 後わずか約 3 kB。バンドルサイズに敏感な SPA やエッジワーカーに最適です。
-
Fetch API ネイティブ Web 標準の Fetch API をラップしているため、Cloudflare Workers・Deno・Bun など あらゆるモダンランタイムで統一的に動作 します。
-
組み込みリトライ ネットワークエラーや 5xx レスポンスに対して自動リトライが組み込まれており、追加パッケージが不要です。
-
UnJS エコシステムとの親和性 Nuxt 3 / Nitro の内部 HTTP クライアントとして採用されており、
$fetchとしてゼロコンフィグで利用できます。 -
型推論の優秀さ TypeScript でゼロから書かれており、ジェネリクスによるレスポンス型の推論が自然です。
4. コード例で比較
4-1. 基本的な GET リクエスト
axios
import axios from "axios";
interface User {
id: number;
name: string;
email: string;
}
// axios.get<T> の T は response.data の型になる
const { data: user } = await axios.get<User>(
"https://jsonplaceholder.typicode.com/users/1"
);
console.log(user.name); // "Leanne Graham"
ofetch
import { ofetch } from "ofetch";
interface User {
id: number;
name: string;
email: string;
}
// ofetch<T> の T は返り値そのものの型になる(data ラッパーなし)
const user = await ofetch<User>(
"https://jsonplaceholder.typicode.com/users/1"
);
console.log(user.name); // "Leanne Graham"
ポイント: axios は
response.dataにアクセスする必要がありますが、ofetch はパース済みデータを直接返します。
4-2. POST リクエスト(JSON 送信)
axios
import axios from "axios";
interface CreatePostBody {
title: string;
body: string;
userId: number;
}
interface Post extends CreatePostBody {
id: number;
}
const { data: newPost } = await axios.post<Post>(
"https://jsonplaceholder.typicode.com/posts",
{
title: "axios vs ofetch",
body: "比較記事を書いています",
userId: 1,
} satisfies CreatePostBody
);
console.log(newPost.id); // 101
ofetch
import { ofetch } from "ofetch";
interface CreatePostBody {
title: string;
body: string;
userId: number;
}
interface Post extends CreatePostBody {
id: number;
}
const newPost = await ofetch<Post>(
"https://jsonplaceholder.typicode.com/posts",
{
method: "POST",
body: {
title: "axios vs ofetch",
body: "比較記事を書いています",
userId: 1,
} satisfies CreatePostBody,
}
);
console.log(newPost.id); // 101
ポイント: axios は
axios.post()のようにメソッド別のショートハンドがあります。ofetch はmethodオプションで指定するスタイルです。
4-3. インスタンス生成と共通設定
axios
import axios from "axios";
const api = axios.create({
baseURL: "https://api.example.com/v1",
timeout: 10_000,
headers: {
Authorization: "Bearer my-token",
},
});
// インターセプターで共通エラーハンドリング
api.interceptors.response.use(
(response) => response,
(error) => {
if (axios.isAxiosError(error) && error.response?.status === 401) {
console.error("認証エラー: トークンを更新してください");
// リフレッシュトークン処理など
}
return Promise.reject(error);
}
);
const { data } = await api.get("/users/me");
ofetch
import { ofetch, FetchError } from "ofetch";
const api = ofetch.create({
baseURL: "https://api.example.com/v1",
timeout: 10_000,
headers: {
Authorization: "Bearer my-token",
},
retry: 2, // 5xx / ネットワークエラー時に最大2回リトライ
async onResponseError({ response }) {
if (response.status === 401) {
console.error("認証エラー: トークンを更新してください");
// リフレッシュトークン処理など
}
},
});
const user = await api("/users/me");
ポイント: どちらもファクトリ関数でインスタンスを生成できます。axios はインターセプターを
use()で後付けし、ofetch はオプションオブジェクト内にフックを宣言します。
4-4. エラーハンドリング
axios
import axios, { AxiosError } from "axios";
try {
await axios.get("https://api.example.com/not-found");
} catch (error) {
if (axios.isAxiosError(error)) {
const axiosErr = error as AxiosError<{ message: string }>;
console.error("ステータス:", axiosErr.response?.status); // 404
console.error("メッセージ:", axiosErr.response?.data?.message); // "Not Found"
} else {
throw error; // axios 以外のエラーは再スロー
}
}
ofetch
import { ofetch, FetchError } from "ofetch";
try {
await ofetch("https://api.example.com/not-found");
} catch (error) {
if (error instanceof FetchError) {
console.error("ステータス:", error.response?.status); // 404
console.error("メッセージ:", error.data?.message); // "Not Found"
} else {
throw error;
}
}
ポイント: axios は
isAxiosError()型ガード、ofetch は標準のinstanceofで判定します。ofetch のFetchErrorはerror.dataにパース済みレスポンスボディが入るため、ワンステップ少なくアクセスできます。
4-5. アップロード進捗の監視(axios のみ)
import axios from "axios";
const formData = new FormData();
formData.append("file", selectedFile);
const { data } = await axios.post("/api/upload", formData, {
headers: { "Content-Type": "multipart/form-data" },
onUploadProgress(progressEvent) {
const percent = Math.round(
(progressEvent.loaded * 100) / (progressEvent.total ?? 1)
);
console.log(`アップロード進捗: ${percent}%`);
},
});
注意: ofetch(Fetch API)にはアップロード進捗を監視する標準的な仕組みがありません。この機能が必須の場合は axios を選択するか、
XMLHttpRequestを直接使う必要があります。
5. どちらを選ぶべきか — ユースケース別ガイド
| ユースケース | 推奨 | 理由 |
|---|---|---|
| Nuxt 3 / Nitro プロジェクト | ofetch | フレームワーク内部で採用済み。$fetch として統合されている |
| Cloudflare Workers / Deno / Bun | ofetch | Fetch API ベースのため追加設定なしで動作する |
| バンドルサイズを極限まで削りたい SPA | ofetch | gzip 後 ≈ 3 kB と圧倒的に軽量 |
| ファイルアップロードで進捗バーが必要 | axios | onUploadProgress が組み込みで提供されている |
| 既存の大規模 React/Vue プロジェクト | axios | 移行コストを考慮。情報量・プラグインが豊富 |
| 複雑なインターセプターチェーン | axios | 複数のインターセプターを順序付きで管理しやすい |
| 自動リトライが欲しい | ofetch | 追加パッケージ不要で組み込みリトライが使える |
| レガシーブラウザ対応が必要 | axios | XMLHttpRequest ベースのため Fetch ポリフィル不要 |
| チームに Fetch API 経験者が多い | ofetch | 標準 API の薄いラッパーなので学習コストが低い |
6. まとめ
| axios | ofetch | |
|---|---|---|
| 一言で言うと | 成熟した万能選手 | 軽量モダンな新鋭 |
axios は 10 年以上の実績と膨大なエコシステムを持ち、「困ったら axios」と言えるほどの安心感があります。一方 ofetch は Web 標準の Fetch API を土台に、リトライ・型推論・マルチランタイム対応といったモダンな要件を最小限のサイズで満たします。
どちらも活発にメンテナンスされており、品質面での優劣はありません。プロジェクトの技術スタック・チームの経験・必要な機能を軸に選択すれば、どちらを選んでも後悔することはないでしょう。