NEXT_PUBLIC_環境変数はサーバーサイドから読み取れない
公式ドキュメントに書いてあることなのですが、ちょっとハマったので共有です。
(元記事はdev.toに投稿したこちらの記事です)
tl;dr
サーバーサイドで使用する環境変数にはNEXT_PUBLICのプレフィックスを付けないこと。
起こったこと
自分のNext.jsプロジェクトで以下のような環境変数を定義していました。
NEXT_PUBLIC_API_BASE_URL=https://(略)/api
APIエンドポイントのベースURLです。
これを以下のように使用していました(実際のコードを簡易化したものですが、やっていることは同じです)。
// In src/utils/env.ts
// 環境変数を返す関数を定義し、アプリケーション内ではその関数を通して環境変数を使用する
export const getApiBaseUrl = (): string => {
if (!process.env.NEXT_PUBLIC_API_BASE_URL) {
throw new Error("NEXT_PUBLIC_API_BASE_URL is undefined");
}
return process.env.NEXT_PUBLIC_API_BASE_URL;
};
そして以下のような関数をサーバーサイドで使用していました。
// API call 関数
// サーバーコンポーネントやサーバーアクションなどサーバーサイドで呼ばれる
export const getEventByUuidService = async (uuid: string): Promise<Event> => {
try {
const res = await fetch(`${getApiBaseUrl()}/event/${uuid}`, { cache: "no-store" });
if (!res.ok) {
throw new Error("Failed to get event by uuid");
}
return (await res.json()).data;
} catch (error) {
throw error;
}
};
ローカルで開発中には何のエラーも出なかったのですが、いざVercelにデプロイするとこのようなエラーログが出ていました。
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11730:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: Error: connect ECONNREFUSED 127.0.0.1:3000
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:128:17) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 3000
}
}
さらにChromeのネットワークタブを確認すると、/api/events/{uuid}に行くはずのリクエストが代わりに/eventsに送られているのが分かりました。つまりNEXT_PUBLIC_API_BASE_URLが無視されていました。
解決方法
公式ドキュメントのこの部分にこのような記載がありました。
To expose an environment variable to the browser, it must be prefixed with NEXT_PUBLIC_. However, these public environment variables will be inlined into the JavaScript bundle during next build.
クライアント側のJSに組み込まれてしまう、つまりサーバーサイドでは読み取れないということです。
なので解決策は単純にNEXT_PUBLICのプレフィックスを付けなければいいという、ただそれだけの話でした(実際にNEXT_PUBLICのプレフィックスを削除すると正常に動作しました)。
結論
クライアント側でアクセスする必要がある環境変数にのみNEXT_PUBLICプレフィックスをつける。
Discussion