axios vs ky ― HTTP クライアント徹底比較【2025年版】
1. 結論
ブラウザ・Node.js 両対応で豊富なエコシステムと実績を重視するなら axios、モダンブラウザ向けに軽量かつ Fetch API ベースのシンプルな設計を求めるなら ky を選びましょう。既存の大規模プロジェクトでは axios の安定感が光り、新規のフロントエンド中心プロジェクトでは ky の軽さと洗練された API が魅力です。
2. 比較表
| 観点 | axios | ky |
|---|---|---|
| GitHub Stars | ≈ 106k | ≈ 14k |
| バンドルサイズ (minified+gzip) | ≈ 13 kB | ≈ 3.3 kB |
| ベースとなる API | XMLHttpRequest / http (Node) | Fetch API |
| Node.js サポート | ✅ ネイティブ対応 | ⚠️ Node 18+ (グローバル fetch 必須) |
| TypeScript 対応 | ✅ 同梱型定義 | ✅ 同梱型定義 |
| リクエスト/レスポンス インターセプター | ✅ 組み込み | ✅ beforeRequest / afterResponse フック |
| リトライ機能 | ❌ 別途プラグイン必要 | ✅ 組み込み (retry オプション) |
| タイムアウト | ✅ timeout オプション | ✅ timeout オプション (デフォルト 10s) |
| アップロード進捗 | ✅ onUploadProgress | ❌ Fetch API の制約で非対応 |
| リクエストキャンセル | ✅ AbortController / CancelToken (非推奨) | ✅ AbortController |
| JSON 自動パース | ✅ 自動 | ✅ .json() ショートハンド |
| HTTP エラーのスロー | ✅ ステータス 2xx 以外で reject | ✅ ステータス 2xx 以外で HTTPError をスロー |
| 学習コスト | 低〜中(情報量が豊富) | 低(API がコンパクト) |
| 週間ダウンロード数 (npm) | ≈ 60M | ≈ 4M |
3. それぞれの強み
axios の強み
-
圧倒的な普及率と情報量 Stack Overflow・ブログ記事・書籍での言及数が桁違いに多く、トラブルシューティングが容易です。
-
Node.js でのフルサポート 内部で
http/httpsモジュールを使うため、Node.js のバージョンを問わず安定して動作します。サーバーサイドでの利用実績も豊富です。 -
アップロード/ダウンロード進捗の取得
onUploadProgress/onDownloadProgressコールバックが組み込みで提供されており、ファイルアップロード UI の実装が容易です。 -
インターセプターの柔軟性 リクエスト・レスポンスそれぞれに複数のインターセプターをチェーンでき、認証トークンの付与やエラーハンドリングの一元管理が簡単です。
-
広範なエコシステム
axios-retry、axios-mock-adapter、axios-cache-interceptorなど、サードパーティプラグインが充実しています。
ky の強み
-
圧倒的な軽量さ gzip 後わずか約 3.3 kB。バンドルサイズを厳しく管理するモダン SPA やライブラリ開発に最適です。
-
Fetch API ネイティブ ブラウザ標準の Fetch API をラップしているため、
Request/Response/Headersといった Web 標準オブジェクトをそのまま扱えます。 -
組み込みリトライ ネットワークエラーや 408 / 413 / 429 / 5xx 系エラーに対するリトライが標準搭載されており、追加パッケージが不要です。
-
洗練されたフック API
beforeRequest、beforeRetry、beforeError、afterResponseの 4 種類のフックが用意されており、宣言的にリクエストライフサイクルを制御できます。 -
.json()ショートハンドメソッドawait ky.get(url).json<T>()のように、レスポンスの取得と型付き JSON パースを 1 行で完結できます。
4. コード例で比較
4-1. 基本的な GET リクエスト
axios
import axios from "axios";
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser(id: number): Promise<User> {
// axios はレスポンスを自動で JSON パースし、data プロパティに格納します
const { data } = await axios.get<User>(
`https://jsonplaceholder.typicode.com/users/${id}`
);
return data;
}
const user = await fetchUser(1);
console.log(user.name); // "Leanne Graham"
ky
import ky from "ky";
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser(id: number): Promise<User> {
// ky は .json<T>() で型付きの JSON パースを 1 行で行えます
return ky
.get(`https://jsonplaceholder.typicode.com/users/${id}`)
.json<User>();
}
const user = await fetchUser(1);
console.log(user.name); // "Leanne Graham"
4-2. POST リクエスト(JSON 送信)
axios
import axios from "axios";
interface CreatePostPayload {
title: string;
body: string;
userId: number;
}
interface Post extends CreatePostPayload {
id: number;
}
async function createPost(payload: CreatePostPayload): Promise<Post> {
const { data } = await axios.post<Post>(
"https://jsonplaceholder.typicode.com/posts",
payload // 第 2 引数にオブジェクトを渡すと自動で JSON シリアライズされます
);
return data;
}
const post = await createPost({
title: "axios で投稿",
body: "本文です",
userId: 1,
});
console.log(post.id); // 101
ky
import ky from "ky";
interface CreatePostPayload {
title: string;
body: string;
userId: number;
}
interface Post extends CreatePostPayload {
id: number;
}
async function createPost(payload: CreatePostPayload): Promise<Post> {
// json オプションにオブジェクトを渡すと Content-Type も自動設定されます
return ky
.post("https://jsonplaceholder.typicode.com/posts", {
json: payload,
})
.json<Post>();
}
const post = await createPost({
title: "ky で投稿",
body: "本文です",
userId: 1,
});
console.log(post.id); // 101
4-3. インターセプター / フックで認証トークンを付与
axios
import axios from "axios";
const api = axios.create({
baseURL: "https://api.example.com",
timeout: 5000,
});
// リクエストインターセプター
api.interceptors.request.use((config) => {
const token = localStorage.getItem("access_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// レスポンスインターセプター(401 時にリフレッシュする例)
api.interceptors.response.use(
(response) => response,
async (error) => {
if (axios.isAxiosError(error) && error.response?.status === 401) {
console.error("認証エラー: トークンを更新してください");
// ここでトークンリフレッシュ処理を実装
}
return Promise.reject(error);
}
);
const { data } = await api.get("/me");
console.log(data);
ky
import ky from "ky";
const api = ky.create({
prefixUrl: "https://api.example.com",
timeout: 5000,
hooks: {
beforeRequest: [
(request) => {
const token = localStorage.getItem("access_token");
if (token) {
request.headers.set("Authorization", `Bearer ${token}`);
}
},
],
afterResponse: [
async (_request, _options, response) => {
if (response.status === 401) {
console.error("認証エラー: トークンを更新してください");
// ここでトークンリフレッシュ処理を実装
}
},
],
},
});
const data = await api.get("me").json();
console.log(data);
4-4. リトライとタイムアウト
axios(axios-retry を併用)
import axios from "axios";
import axiosRetry from "axios-retry";
const client = axios.create({ timeout: 5000 });
// axios 単体にはリトライ機能がないため、axios-retry を追加します
axiosRetry(client, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay, // 指数バックオフ
retryCondition: (error) =>
axiosRetry.isNetworkOrIdempotentRequestError(error) ||
error.response?.status === 429,
});
const { data } = await client.get("https://api.example.com/data");
console.log(data);
ky(組み込みリトライ)
import ky from "ky";
// ky は追加パッケージ不要でリトライを設定できます
const data = await ky
.get("https://api.example.com/data", {
timeout: 5000,
retry: {
limit: 3,
methods: ["get"],
statusCodes: [408, 413, 429, 500, 502, 503, 504],
backoffLimit: 3000, // バックオフの上限 (ms)
},
})
.json();
console.log(data);
5. どちらを選ぶべきか ― ユースケース別ガイド
| ユースケース | 推奨 | 理由 |
|---|---|---|
| Node.js サーバーサイド (Express / NestJS など) | axios | Node.js 全バージョンで安定動作。ストリーム処理やプロキシ設定も充実 |
| モダンブラウザ向け SPA (React / Vue / Svelte) | ky | 軽量でバンドルサイズを抑えられる。Fetch API ベースで Web 標準に沿った設計 |
| ファイルアップロードで進捗表示が必要 | axios | onUploadProgress が組み込みで提供されている |
| ライブラリ / SDK の内部 HTTP クライアント | ky | 依存サイズが小さく、利用者のバンドルへの影響を最小化できる |
| 既存の大規模プロジェクトへの導入 | axios | 情報量・チームの習熟度・既存コードとの整合性で有利 |
| Cloudflare Workers / Deno / Edge Runtime | ky | Fetch API ベースのため、非 Node.js ランタイムとの相性が良い |
| リトライ・バックオフを手軽に実装したい | ky | 追加パッケージなしで宣言的にリトライを設定可能 |
| レガシーブラウザ (IE11 等) のサポートが必要 | axios | XMLHttpRequest ベースのため Polyfill なしで動作する |
6. まとめ
axios と ky はどちらも優れた HTTP クライアントですが、設計思想が異なります。
-
axios は「あらゆる環境で動く万能ナイフ」です。Node.js からレガシーブラウザまで幅広くカバーし、エコシステムの厚さと情報量の多さが最大の武器です。チーム開発や長期運用のプロジェクトでは、この安定感が大きなアドバンテージになります。
-
ky は「Web 標準に寄り添った軽量スカルペル」です。Fetch API をベースにした薄いラッパーでありながら、リトライ・フック・型付き JSON パースといった実用的な機能を備えています。バンドルサイズの制約が厳しいフロントエンドや Edge Runtime 環境では、ky の軽さが際立ちます。
最終的には、プロジェクトの実行環境・チームの技術スタック・バンドルサイズの要件を総合的に判断して選択してください。どちらを選んでも、素の fetch を直接使うよりも生産性の高い HTTP 通信を実現できるはずです。