Nuxt 3 の nitro preview で fetch が動かない時の原因と解決策

Nuxt 3 では、アプリケーションをビルドしたあとに npm run build && npm run preview(もしくは nuxt preview)で “preview” 環境を立ち上げ、動作を確認することができます。
しかしこの「preview」環境で、$fetch または useFetch を使った API 通信が「反応しない」「エラーになる」ケースが報告されています。
この記事では、特に「nitro preview 実行時」に発生しやすい問題を深掘りし、原因と実務的な解決策を整理します。

問題の概要

  • 開発モード(npm run dev)では正常に動いていた useFetch('/api/xxx') が、ビルド後の preview モードで動かない。
  • useFetchuseAsyncData を使っても、データが取得できず null またはエラーになる。
  • $fetch と比較して、useFetch のほうが失敗しやすい場面がある。
  • 特に、内部 API(/api/ 配下)への呼び出しで発生することが多い。

このような状況は「preview =本番近似環境」で動作させようとしたときに発覚するため、実運用前のチェックポイントとして重要です。

なぜ “preview” 環境で動かなくなるのか

Nitro サーバ/preview モードの構成が dev モードと異なる

開発モードでは、Vite 開発サーバと Nitro サーバが同一プロセスまたは密接に連携しており、/api/xxx のような内部ハンドラに対しては “ネットワークを跨がない呼び出し” が行われることがあります。
一方で、preview モード(nitro preview)では通常、次のような構成に近くなります:

ブラウザ → Nitro HTTP サーバ → API ハンドラ

このとき、内部 API 呼び出しが HTTP 経由で自コンテナ/プロセスにアクセスされる形になるため、開発モードと比べて以下のようなミス・制約が表面化します:

  • 相対パス(/api/...)だけでアクセスすると、ホスト名/ポート/プロトコルの解決が開発時と異なる。
  • Docker/コンテナ環境やリバースプロキシ構成だと、localhost127.0.0.1 が正しいホストではなかったり、Netlify/Vercel などのサーバレス環境では自己リクエストが制限されていたりします。
  • useFetch(または useAsyncData)が SSR(サーバサイド)コンテキストで実行されると、ブラウザと異なる環境からの呼び出しになるため、クロスオリジン/ホスト名/リダイレクトの問題が顕在化します。実際、Stack Overflow の報告でも「refresh/直アクセス時に SSR で失敗、クライアント側では成功した」というケースが確認されています。 Stack Overflow+1

$fetchuseFetch の挙動差

  • $fetch は比較的 “汎用 HTTP クライアント” として動作し、クライアント/サーバ両方から使えます。
  • useFetch は Nuxt の高レベル構成要素で、SSR・クライアント両方でデータ取得および差分更新(reactivity)を担うため、サーバ側の実行時点(プロセス、環境変数、ホスト解決など)に強く依存します。
    そのため、preview 環境で useFetch が失敗しても $fetch は動いたという報告も多く、「なぜ useFetch は失敗するのか?」の理解がポイントになります。 Reddit+1

実務的な解決策

解決策 1:ランタイム設定(runtimeConfig)でベースURLを明示

環境ごとに API のベース URL を切り替えられるようにし、preview 環境でも正しいホスト/プロトコルでアクセスできるようにします。

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      apiBase: process.env.NUXT_PUBLIC_API_BASE || 'http://localhost:3000'
    }
  }
})
// 任意の composable やページ
const config = useRuntimeConfig()
const { data, error } = await useFetch(`${config.public.apiBase}/api/todos`)

本番・preview 環境では NUXT_PUBLIC_API_BASEhttps://yourdomain.com などに設定しておけば、相対パスだけに頼って起きる “アクセス先ホストの誤り” を防げます。

解決策 2:Nitro の設定を確認/修正

preview モードでは nitro.config.tsnuxt.config.tsrouteRulesproxydevProxy が関係してきます。たとえば:

// nitro.config.ts
export default defineNitroConfig({
  devProxy: {
    '/api': {
      target: 'http://localhost:3000',
      changeOrigin: true
    }
  }
})

あるいは:

export default defineNuxtConfig({
  routeRules: {
    '/api/**': { cors: true, proxy: 'http://localhost:3000/api/**' }
  }
})

このように、preview や本番環境で “自己呼び出し” が正しくルーティングされるかをチェックします。API が別コンテナにあったりプロキシが必要なときは特に重要です。

解決策 3:相対パスではなく絶対URLを使う/内部関数呼び出しに変更

相対パスだけだとホスト解決があいまいになりがちです。以下のいずれかを検討してください:

  • useFetch(${config.public.apiBase}/api/…) のように絶対 URL を使う。
  • サーバサイド実行(API ハンドラ内)では $fetch を使うのではなく、直接内部関数を import して呼び出す(自己リクエストを避ける)。たとえば:
// server/api/tasks.ts
export default defineEventHandler(() => { /* … */ })

// page or composable
import { getTasks } from '~/server/api/tasks'
const tasks = await getTasks()

こうすることで SSR 時の “HTTP 経由” ではなく、直接関数呼び出しとなり、環境差異を減らせます。

解決策 4:Docker/コンテナ環境・ホスト名の確認

Docker やクラウド環境(Netlify Edge、Vercel 等)では、localhost が適切なホスト名でない場合があります。Stack Overflow では「localhost127.0.0.1 に変更して動いた」という投稿もあります。 Stack Overflow
ポートフォワーディング、コンテナ名、プロキシ設定などを見直してください。

まとめ

Nuxt 3 の nitro preview 環境で fetch が動かない原因は、主に「開発モードと異なる実行コンテキスト」「ホスト/プロトコル/HTTP リクエスト構造が変わる」ことにあります。
特に useFetch を用いた SSR 領域でのデータ取得では、環境変数・ベースURL・プロキシ設定を理解しておくことで多くのトラブルを防げます。

ポイント整理

  • preview 環境では HTTP 経由で API が呼ばれる構成となることが多い。
  • useFetch は SSR 実行時の環境に依存して失敗しやすい。
  • runtimeConfig でベースURLを環境に応じて切り替える。
  • Nitro のプロキシ/routeRules を確認して内部APIのルーティングを確保。
  • コンテナ・クラウド環境ではホスト名(localhost vs 127.0.0.1)にも注意。

これらを押さえておくことで、preview 環境だけでなく本番運用でも同様の通信エラーを未然に防ぐ設計が可能です。

タイトルとURLをコピーしました