Next.jsのNEXT_PUBLICの環境変数値はビルド時に決定する
Next.jsで扱える環境変数
サーバーのみで使える変数と、クライアントでも使える変数の2種類がある。
種類 | 内容 | 使用可能な場所 |
---|---|---|
process.env.VARIABLE_NAME | サーバーサイドのみで利用可能 | API Routes, サーバーコンポーネント |
NEXT_PUBLIC_VARIABLE_NAME | クライアントサイドでも利用可能 | useEffect, フロントエンドのコード |
今回問題となった事象
NEXT_PUBLIC_プレフィックスのついた環境変数の値が、undefined
となっており、環境変数の読み込みがうまくできていないことが発覚。アプリケーションの主な環境は以下。
- フロントエンド:Next.js 15
- バックエンド:Nest.js, prisma
- インフラ:AWS
- 環境変数などの値は
AWS Systems Manager Parameter Store
で管理
- 環境変数などの値は
- その他:Docker, GitHub Actions
環境変数の読み込みができていない原因を調べていると、タイトルに記載の通り、NEXT_PUBLICプレフィックスがついた環境変数はビルド時に決定されることが原因であることがわかった。
今回作成しているアプリケーションのCI/CDに関して、以下の手順で行われている。
- mainブランチにマージ
- GitHub Actionsでアプリケーションのビルド -> ECRへのpush
- ECRへのpushがトリガーに作動するCodePipelineが起動し、デプロイ
ビルド時に環境変数が確定するが、手順2の時点では環境変数値を管理しているParameter Storeを参照する手順はなく、値が設定されないままアプリケーションが起動していた。
課題に対する解決策
以下の3つの案を検討した。
- GitHub Actionsのビルド時に、AWS CLIでParameter Storeから環境変数を取得しセットする
- Dockerのentrypoint.shで環境変数を適用する
- 環境変数取得用のAPI Routeを作成する
結論として、3を採用して実装を行なった。1,2を採用しなかった理由は、NEXT_PUBLIC_がついた環境変数値は、ブラウザに値が公開されてしまうから。
以下公式ドキュメントの引用。
Non-NEXT_PUBLIC_ environment variables are only available in the Node.js environment, meaning they aren't accessible to the browser (the client runs in a different environment).
NEXT_PUBLIC_以外の環境変数はNode.js環境でのみ利用可能で、ブラウザからはアクセスできません(クライアントは別の環境で実行されます)。
In order to make the value of an environment variable accessible in the browser, Next.js can "inline" a value, at build time, into the js bundle that is delivered to the client, replacing all references to process.env.[variable] with a hard-coded value. To tell it to do this, you just have to prefix the variable with NEXT_PUBLIC_.
ブラウザから環境変数の値にアクセスできるようにするために、Next.jsはビルド時に、クライアントに配信されるjsバンドルに値を "インライン "し、process.env.[変数]への参照をすべてハードコードされた値に置き換えることができます。 Next.jsは、クライアントに配信されるjsバンドルのビルド時に値を「インライン化」することができます。
これらから、環境変数取得用のAPI Routeを作成し、クライアントサイドで使いたい場合はfetch関数を使って取得することで対応した。
export async function GET() {
return NextResponse.json({
clientId: process.env.CLIENT_ID || ''
})
}
const fetchConfig = async () => {
const res = await fetch('api/env')
const data = await res.json()
console.log(data.clientId)
}
参考資料
Discussion