ofetch の使い方 — Node.js・ブラウザ・Workerで動く高機能fetchラッパー
一言でいうと
ofetch は、Node.js・ブラウザ・Web Workers のすべてで動作する、ネイティブ fetch API の上位互換ラッパーです。JSONの自動パース、自動リトライ、エラーハンドリング、インターセプターなど、実務で必要な機能が最初から揃っています。
どんな時に使う?
- Nuxt / Node.js / Edge Workers で共通のHTTPクライアントを使いたい時 — 環境差を吸収してくれるため、同じコードがどこでも動きます(Nuxt 3の内部でも採用されています)
- axios から fetch ベースのライブラリに移行したい時 — インターセプター、自動リトライ、baseURL設定など、axiosユーザーが期待する機能を備えています
- 素のfetchのボイラープレートを減らしたい時 — レスポンスのJSON自動パース、エラー時の自動throw、Content-Typeの自動設定など、毎回書いていた定型処理が不要になります
インストール
# npm
npm i ofetch
# yarn
yarn add ofetch
# pnpm
pnpm add ofetch
ofetch の基本的な使い方
import { ofetch } from "ofetch";
// GETリクエスト — レスポンスは自動でJSONパースされる
interface User {
id: number;
name: string;
email: string;
}
const users = await ofetch<User[]>("/api/users", {
baseURL: "https://api.example.com",
});
console.log(users[0].name); // 型補完が効く
// POSTリクエスト — bodyは自動でJSON.stringifyされ、Content-Typeも自動設定
const newUser = await ofetch<User>("/api/users", {
baseURL: "https://api.example.com",
method: "POST",
body: { name: "Taro", email: "taro@example.com" },
});
これだけで、素の fetch で必要だった以下の処理がすべて自動化されます:
response.okチェック → 失敗時は自動で例外をスローresponse.json()呼び出し → Content-Typeに応じて自動パースJSON.stringify(body)→ オブジェクトを渡すだけでOKheaders: { "Content-Type": "application/json" }→ 自動設定
よく使うAPI
1. ofetch<T>(url, options) — 基本のリクエスト
import { ofetch } from "ofetch";
// 型パラメータでレスポンスの型を指定
interface Article {
id: number;
title: string;
body: string;
}
const article = await ofetch<Article>("/api/articles/1", {
baseURL: "https://api.example.com",
});
2. query / params — クエリパラメータの付与
// /api/articles?page=2&limit=10&lang=ja として送信される
const articles = await ofetch<Article[]>("/api/articles", {
baseURL: "https://api.example.com",
query: {
page: 2,
limit: 10,
lang: "ja",
},
});
// URL自体にクエリがあっても安全にマージされる
// → /api/articles?lang=en&id=123
await ofetch("/api/articles?lang=en", { query: { id: 123 } });
params は query のエイリアスです。どちらを使っても同じ動作になります。
3. ofetch.create() — デフォルトオプション付きインスタンス生成
const apiFetch = ofetch.create({
baseURL: "https://api.example.com/v1",
headers: {
Authorization: "Bearer my-token",
},
retry: 2,
retryDelay: 500,
});
// 以降はbaseURLとヘッダーが自動付与される
const users = await apiFetch<User[]>("/users");
const articles = await apiFetch<Article[]>("/articles");
4. インターセプター — リクエスト/レスポンスのフック
const apiFetch = ofetch.create({
baseURL: "https://api.example.com",
async onRequest({ options }) {
// 毎リクエストに認証トークンを付与
const token = await getAccessToken();
options.headers.set("Authorization", `Bearer ${token}`);
},
async onRequestError({ request, error }) {
console.error(`[Request Error] ${request}`, error);
},
async onResponse({ request, response }) {
console.log(`[Response] ${request} — ${response.status}`);
},
async onResponseError({ request, response }) {
// 401なら認証エラーハンドリング
if (response.status === 401) {
await redirectToLogin();
}
},
});
インターセプターは配列で複数指定することも可能です:
await ofetch("/api/data", {
onRequest: [
({ options }) => { /* ロギング */ },
({ options }) => { /* トークン付与 */ },
],
});
5. リトライ・タイムアウト設定
const data = await ofetch("https://api.example.com/unstable-endpoint", {
// リトライ回数(デフォルト: GETは1、POST/PUT/PATCH/DELETEは0)
retry: 3,
// リトライ間隔(ミリ秒、デフォルト: 0)
retryDelay: 1000,
// リトライ対象のステータスコードをカスタマイズ
retryStatusCodes: [408, 429, 500, 502, 503, 504],
// タイムアウト(ミリ秒、デフォルト: 無効)
timeout: 5000,
});
補足: レスポンスタイプの制御
// Blobとして取得(画像・ファイルダウンロードなど)
const imageBlob = await ofetch("/api/avatar.png", {
responseType: "blob",
});
// ReadableStreamとして取得(ストリーミング処理)
const stream = await ofetch("/api/large-data", {
responseType: "stream",
});
// テキストとして取得(JSONパースをスキップ)
const html = await ofetch("/page", {
responseType: "text",
});
// カスタムパーサーを使用
const data = await ofetch("/api/data", {
parseResponse: (text) => JSON.parse(text),
});
類似パッケージとの比較
| 特徴 | ofetch | axios | ky | node-fetch |
|---|---|---|---|---|
| ブラウザ対応 | ✅ | ✅ | ✅ | ❌ |
| Node.js対応 | ✅ | ✅ | ❌(v1時点) | ✅ |
| Web Workers / Edge | ✅ | ❌ | ✅ | ❌ |
| ベース | fetch API | XMLHttpRequest | fetch API | fetch polyfill |
| JSON自動パース | ✅ | ✅ | ✅ | ❌ |
| 自動リトライ | ✅ | ❌(別途プラグイン) | ✅ | ❌ |
| インターセプター | ✅ | ✅ | ✅(hooks) | ❌ |
| TypeScript | ✅ ネイティブ | ✅ | ✅ | △(@types必要) |
| バンドルサイズ | ~1KB (min+gzip) | ~13KB | ~3KB | N/A |
| Nuxt 3統合 | ✅(内蔵) | ❌ | ❌ | ❌ |
選定の目安:
- Nuxt 3 / Nitro を使っている →
ofetch一択(内部で使われている) - ブラウザ + Node.js + Edge の全環境で統一したい →
ofetch - XMLHttpRequestの機能(アップロード進捗など)が必要 →
axios - ブラウザ専用で軽量なものが欲しい →
ky
注意点・Tips
エラーハンドリングは FetchError を活用する
import { ofetch, FetchError } from "ofetch";
try {
await ofetch("/api/protected");
} catch (error) {
if (error instanceof FetchError) {
// error.data にパース済みのレスポンスボディが入る
console.error(`Status: ${error.response?.status}`);
console.error(`Error body:`, error.data);
}
}
POST/PUT/PATCH/DELETE はデフォルトでリトライしない
副作用のあるメソッドは安全のためリトライ回数が 0 に設定されています。明示的に retry を指定すると、冪等でないリクエストが重複実行される可能性があるため注意してください。
ofetch.create() のヘッダーは浅いマージ
create() で設定したデフォルトオプションは1階層のみクローンされます。ネストされたオブジェクト(特に headers)は参照が共有される場合があるため、動的にヘッダーを変更する場合はインターセプター(onRequest)で設定するのが安全です。
destr による安全なJSONパース
ofetch は内部で destr を使ってJSONをパースします。これは JSON.parse と異なり、プロトタイプ汚染攻撃に対する防御が組み込まれています。厳密に JSON.parse を使いたい場合は parseResponse オプションで上書きできます。
レスポンスのステータスエラーを無視したい場合
// response.ok が false でも例外をスローしない
const response = await ofetch("/api/maybe-404", {
ignoreResponseError: true,
});
まとめ
ofetch は、Node.js・ブラウザ・Edge Workers のすべてで統一的に使えるfetchラッパーで、JSON自動パース・自動リトライ・インターセプター・型安全なレスポンスといった実務に必要な機能を極めて小さなバンドルサイズで提供します。特にNuxt 3エコシステムでは標準のHTTPクライアントとして組み込まれており、素の fetch のボイラープレートから解放されつつ、axiosのような使い勝手を得られるモダンな選択肢です。