Next.js にて環境変数を環境ごとにうまく管理したい

環境ごとに環境変数を管理したく、
色々調べた結果、なんとなく方向性が決まったのでメモ。

やりたいこと

  • 少なくとも、開発用、ステージング用、本番用で環境変数を変えたい
  • 人為的エラーは少なくしたい
  • 各サーバーでの作業を減らしたい

環境

  • Next.js 15.0.2

.env

Next.js では、
環境変数は .env のようなファイルを用意して扱うらしい。

置く場所

Note: If you are using a /src folder, please note that Next.js will load the .env files only from the parent folder and not from the /src folder. This loads process.env.DB_HOST, process.env.DB_USER, and process.env.DB_PASS into the Node.js environment automatically allowing you to use them in Route Handlers.

/path/to/your/project/app に .next やら package.json やらが詰まった Next.js のプロジェクトがあれば、
/path/to/your/project/app に .env などのファイルを置くらしい。
/path/to/your/project/app/src に置くと読み込まれない。
とのこと。

ファイル名

Environment variables are looked up in the following places, in order, stopping once the variable is found.

  1. process.env
  2. .env.$(NODE_ENV).local
  3. .env.local (Not checked when NODE_ENV is test.)
  4. .env.$(NODE_ENV)
  5. .env
  • 上記のファイル名パターンが対象。とのこと。
  • 各ファイル間で変数名が重複したら上のパターンから優先される。
  • .env.local は test には含まれない
  • process.env はファイル名ではない。けど何を指しているのか分からない。

Good to know: The allowed values for NODE_ENV are production, development and test.

NODE_ENV は「production」「development」「test」の3種類のみ。
とのこと。

使い方

# .env
TEST=test
NEXT_PUBLIC_TEST=next public test

サーバーサイドレンダリング環境下では意識しなくていいけど、
"use client" で使う時は、頭文字に「NEXT_PUBLIC_」をつける。
取得できない変数は undefined になる。

// /src/app/page.tsx

export default function Home() {
	console.log(process.env.TEST)// test
	console.log(process.env.NEXT_PUBLIC_TEST)// next public test
	return (
		<>
			{ process.env.TEST }<br/>
			{ process.env.NEXT_PUBLIC_TEST }<br/>
		</>
	)
}

"use client" の場合。

// /src/features/something/index.tsx

"use client" 

export default function Something() {
	console.log(process.env.TEST)// undefined
	console.log(process.env.NEXT_PUBLIC_TEST)// next public test
	return (
		<>
			{ process.env.TEST }<br/>
			{ process.env.NEXT_PUBLIC_TEST }<br/>
		</>
	)
}

方向性

  • gitignore で .env* と記述されていたけど git で .env ファイルを管理する
    • 迷ったけど、一旦、一部 .env ファイルは管理する方向で。
  • NODE_ENV は意識させない
    • これに依存させると、かゆい所に手が届かなくなる
  • APP_ENV などの独自の環境変数は使わない
    • こういうの導入すると地獄を見る気がする
  • next.config.ts には記述しない
  • dotenv 系は使わない

ファイルを用意する

$ touch .env.test
$ touch .env.dev
$ touch .env.stg
$ touch .env.prod
# .env.test
NEXT_PUBLIC_TEST=test
# .env.dev
NEXT_PUBLIC_TEST=development
# .env.stg
NEXT_PUBLIC_TEST=staging
# .env.prod
NEXT_PUBLIC_TEST=production

.gitignore

プロダクトの内容によっては .env.sample のようなファイルを用意して、
中身をダミーデータにする。その場合 .gitignore に追加するのはダミーデータのファイルのみ。

# env files (can opt-in for commiting if needed)
.env*

# 追加
!.env.test
!.env.dev
!.env.stg
!.env.prod

packege.json

コマンドで読み込む .env.local ファイルの内容を変える。
これのデメリットはホットリロードに対応していないので、
環境変数を変えたらコマンドを打ち直す必要があること。

{
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  }
}

{
  "scripts": {
    "dev": "rm -f .env.local && cp .env.dev .env.local && next dev --turbopack",
    "dev:stg": "rm -f .env.local && cp .env.stg .env.local && next dev --turbopack",
    "build": "rm -f .env.local && cp .env.dev .env.local && next build",
    "build:stg": "rm -f .env.local && cp .env.stg .env.local && next build",
    "build:prod": "rm -f .env.local && cp .env.prod .env.local && next build",
    "start": "next start",
    "lint": "next lint"
  }
}
ex)
$ npm run build:stg
$ npm run start