😸
なぜ Next.js で axios を入れず、fetch を薄くラップしたか

要約
- 前提: Next.js 13+(App Router)、TypeScript、SSR/RSC を併用
-
判断: 外部ライブラリの
axiosは導入せず、組み込みのfetchを小さくラップして利用 -
理由: Next.js の実行・キャッシュモデルに
fetchが自然に統合。私たちのユースケースでは 必要十分 - 効果: 依存・設定を最小化しつつ、共通ヘッダー/エラー処理/型をひとつのレイヤーに集約
対象読者 / 想定環境
- 対象: Next.js(App Router)で SSR/RSC を使う小~中規模の Web アプリ
- 環境: Next.js 13 以上、TypeScript、Node.js 18+
- ゴール: 余計な依存を増やさず 一貫したリクエスト層 を整える
背景と判断の軸
多くのプロジェクトでは慣例的に axios を入れます。しかし、私たちの要件(シンプルな CRUD が中心・複雑なインターセプタ連鎖は不要)では、**「組み込み fetch + 薄いラッパ」**のほうが合理的でした。
1) Next.js のモデルに自然に乗る
- App Router/RSC では サーバー側の
fetchが自動キャッシュ されるなど、実行時最適化と密接 -
fetch(url, { cache: 'no-store' })など 公式オプション で挙動を制御
→ ランタイムに近い層へ依存を増やさず、Next.js 本来の最適化を活かせる
2) “いま必要な機能” に絞れる
-
axiosのインターセプタやタイムアウト等は便利だが、私たちのケースではfetch+ 小さなユーティリティ で十分 - 余分な依存や設定を足さず、シンプルさ と 可読性 を優先
3) 両環境で一貫して使える
- サーバー/クライアント双方で
fetchが使え、API は一つ で済む - 環境分岐が減り、実装・レビューコスト を抑制
実装:customFetch(最小限のラッパ)
目的は「共通ヘッダー・エラーハンドリング・返却型の統一」。大きくし過ぎず、“必要になったら足す” 方針。
// lib/custom-fetch.ts
export async function customFetch<T>(
input: RequestInfo,
init?: RequestInit
): Promise<T> {
const res = await fetch(input, {
...init,
headers: {
'Content-Type': 'application/json',
...(init?.headers || {}),
},
});
if (!res.ok) {
const errorText = await res.text();
throw new Error(`[${res.status}] ${errorText}`);
}
return res.json() as Promise<T>;
}
**ポイント**
* **共通ヘッダー** を一箇所で付与
* **エラー整形** を統一(ログ/トーストの起点をここに集約可能)
* **ジェネリクス** により返却型を明示し、呼び出し側の推論を補助
---
## 使用例
```ts
import { customFetch } from '@/lib/custom-fetch';
type User = { id: string; name: string };
export const getUser = async () => {
const data = await customFetch<User>('/api/user');
return data;
};
- 呼び出し側は 型の意図 が読みやすく、テスト観点でも扱いやすい
- 再試行(retry)や計測(metrics)を足す場合も 注入ポイントは一箇所
環境ごとの分岐(必要なら)
const isServer = typeof window === 'undefined';
const baseUrl = isServer ? process.env.API_URL : '';
await fetch(baseUrl + '/api/endpoint');
まずは最小構成で始め、要件が増えたら 小さく育てる 方針が破綻しにくいと感じました。
どこまでを fetch で賄い、どこから拡張するか
-
まずは fetch:キャッシュ/再検証や RSC の挙動まで含め、Next.js との親和性が高い
-
必要に応じて拡張:
- 再試行・バックオフ、署名、観測(OpenTelemetry 等)は
customFetchに積み上げる - それでも不足する複雑要件(高度なインターセプトや独自トランスポート)が出た時点で専用ライブラリ導入を検討
- 再試行・バックオフ、署名、観測(OpenTelemetry 等)は
「いま必要な最小」を守ると、依存の寿命 と 保守コスト の見通しが良くなる
まとめ
- Next.js の強み(
fetchと実行モデルの統合)を活かすため、axiosは入れず 薄いcustomFetchを採用 - 小さなラッパでも 共通化(ヘッダー/エラー/型) の恩恵は十分
- シンプルさと拡張性 を両立しやすい運用パターン
付記(Disclosure)
本記事は 技術共有 を目的としています。
個人的にリンク管理ツール Link Dropper を運用しています。関心があればこちらをご覧ください(簡単な紹介のみ)。
👉 https://link-dropper.com
※ 記事本体は特定サービスの宣伝を主目的としていません。内容の正確性・再現性に関するご指摘は歓迎します。
運用メモ(所感)
-
レビュー容易性:
customFetchの差分は小さく、PR で議論しやすい - ランタイム差異の吸収:Node/ブラウザ差はラッパ内で吸収し、呼び出し側は意識不要
- 監視の入口:レスポンス時間・失敗率の計測追加も 一箇所で完結
仕様の更新に追従するため、キャッシュ/再検証や RSC の挙動は Next.js 公式ドキュメントを都度確認するのがおすすめです。
Discussion