process.env は、シェルがNode.jsのプログラムに渡した環境変数を1つずつプロパティとして持つオブジェクトです。process.env.PORT、process.env.DATABASE_URL のように読みます。設定されていない変数は undefined になり、設定されている変数は必ず文字列です。起動時に1回読み、デフォルト値を与え、必要な型に変換しましょう。
shop on port 8080 in eu-west と表示されました(REGION は設定されていないのでデフォルト値が使われました)。続いて string number と表示されています。変数は Number() で変換するまでテキストのままです。最初の2つの代入を消して実行し直すと、すべてデフォルト値になります。
シェルで環境変数を設定する
変数は、Node.jsを起動した場所から来ます。ターミナルでは次のようにします。
// macOS and Linux: for one command
PORT=8080 NODE_ENV=production node app.js
// macOS and Linux: for the rest of the terminal session
export PORT=8080
// Windows PowerShell
$env:PORT = "8080"; node app.js
// Windows cmd.exe
set PORT=8080
node app.js
ホスティング環境(Heroku、Render、Vercel、Docker、Kubernetes)には、同じことをする設定画面や設定ファイルがあります。コード側の読み方は変わりません。
すべては文字列
process.env のバグの大半はここから生まれます。'false' と '0' は空でない文字列なので、truthyです。
素朴なチェックでは、false に設定した変数でも debug is ON と表示されました。期待する文字列そのものと比較しましょう。数値の場合、Number(process.env.X) は値がないと NaN になるので、最初の例で ?? を使ったように、変換する前にデフォルト値を適用します。
NODE_ENV
NODE_ENV はNode.jsの機能ではなく慣習です。Node.jsがこれを設定することはありません。読むのはライブラリです。たとえばExpressは、これが production だとエラーページにスタックトレースを出さなくなり、多くのツールは開発時だけのチェックを省きます。
mode: production と verbose logs: false が表示されました。'production' を 'development' に変えると、2行目が逆になります。ライブラリはこの単語そのものを確認するので、production、development、test のどれかを使いましょう。
.envファイル:--env-fileとdotenv
node の前に毎回すべての変数を書くのは面倒です。.env ファイルなら、プロジェクトのフォルダに1行に1つずつ置いておけます。
// .env
PORT=8080
DATABASE_URL=postgres://localhost/shop
API_KEY=dev-key-123
Node.js 20.6以降は、パッケージなしでフラグ1つで読み込めます。
node --env-file=.env app.js
古いバージョンの場合や、こちらのほうが好みなら、dotenv パッケージをインストールし(npm install dotenv)、エントリーファイルの一番上、process.env を読むコードより前で読み込みます。
require('dotenv').config(); // copies .env into process.env
console.log(process.env.PORT); // 8080
どちらもシェルですでに設定されている変数は上書きしないので、本番の設定がファイルより優先されます。
足りない変数は早めに失敗させる
データベースのURLがないなら、1時間後の最初のリクエストでクラッシュするのではなく、起動時にわかりやすいメッセージを出して止まるべきです。
Missing environment variables: API_KEY と表示されました。先頭に process.env.API_KEY = 'test'; を足すと Config OK と表示されます。実際のプログラムではメッセージの後に process.exit(1) を書きましょう。デプロイスクリプトやコンテナ基盤は、0以外の終了コードを見て起動に失敗したと判断するからです。
秘密情報は絶対にコミットしない
.env は作ったその日に .gitignore に入れましょう。代わりに、変数名とダミーの値を書いた .env.example をコミットしておけば、ほかの人も何を設定すればいいかわかります。一度でもリポジトリにプッシュしたキーは、後でコミットを消したとしても漏れたものとして扱い、作り直してください。
よくある質問
process.env.MY_VARがundefinedになるのはなぜですか?
Node.jsを起動したシェルで変数が設定されていないか、別のターミナルで設定されています。.env ファイルも自動では読まれません。値を読む前に、--env-file=.env を付けてNode.jsを起動するか、dotenv で読み込んでください。
実行中にprocess.envを変更できますか?
できます。process.env.X = 'value' と代入すると、今のプロセスと、その後に起動する子プロセスでは値が変わりますが、Node.jsを起動したシェルには影響しません。代入した値はNode.jsが文字列に変換します。
環境変数の名前は大文字と小文字を区別しますか?
LinuxとmacOSでは区別します。PORT と port は別の変数です。Windowsでは区別しません。アンダースコア入りの大文字の名前に統一しておけば、この問題は起きません。
ブラウザからprocess.envを読めますか?
読めません。ブラウザには環境変数がありません。ViteやNext.jsのようなビルドツールは、選んだ変数をビルド時にフロントエンドのコードへコピーします。つまり、その値は公開されるということです。