Node.jsで環境変数ファイルを読み込むdotenvを使って、複数回読み込みをおこなったときにどの環境変数ファイルの環境変数の値が有効になるのかを検証しました。
また、Node.js v20.6.0から追加された--env-file
オプションによる環境変数ファイルの読み込みも試しました。
はじめに
dotenvは、環境変数が保存されているファイル(デフォルトでは.env
)を読み込んで変数process.env
に格納してくれるモジュールです。
.env
ファイルを変更することで、ソースコードを変更することなく設定を変更することができます。
例えば、.env
ファイルに以下のように書き込みます。
PORT=3000
JavaScriptファイルでは以下のようにすることで、.env
ファイルの値を利用できるようになります。
require('dotenv').config(); // ESMなら以下のようにする // import 'dotenv/config'; console.log(process.env.PORT); // 3000
変数process.env
は本来、Node.jsをCLIを使って呼び出したときの環境変数が格納されます。
例えば、index.js
を以下のようにします。
console.log(process.env.PORT);
このファイルを以下のように環境変数を指定して呼び出すとその値が格納されていることがわかります。
$ PORT=3000 node index.js 3000
また、Node.jsにはv20.6.0から追加された--env-file
オプションが存在し、こちらもdotenvと同じように指定されたファイルを読み込んで、変数process.env
に格納します。
このオプションはStability 1とされており、まだ実験段階(Experimental)とされています。Stableにはなっていないため、ご注意ください。
.env
ファイル、index.js
はこれまでと同じにします。
CLIで--env-file
オプションを使うことで.env
ファイルを読み込んで利用できるようになります。
$ node --env-file=.env index.js 3000
検証
検証に使ったコードはGitHubにあります。
使った環境のバージョンは以下のとおりです。
- Node.js v20.17.0
- dotenv v16.4.5
また、使った環境変数ファイルは3つあり、先に紹介しておきます。
.env
ファイル
MY_ENV_VALUE=DOTENV_FILE ALPHA=AAA
.env.next
ファイル
MY_ENV_VALUE=DOTENV_NEXT_FILE BETA=BBB
.env.next2
ファイル
MY_ENV_VALUE=DOTENV_NEXT2_FILE CHARLIE=CCC
dotenvで環境変数ファイルを複数回読み込むと先勝ち
以下のように.env
ファイル、.env.next
ファイルの順で読み込みます。
(dotenvではpath
というオプションによって読み込みをおこなうファイルパスを指定できます。)
2つの環境変数ファイルで重複しているキーは、先に読み込まれたファイルの値が優先されます。
以下の例だと、MY_ENV_VALUE
が重複しており、先に読み込んだ.env
ファイルの値が使われています。
また、後の環境変数ファイルに存在しないキーも先に読み込まれたファイルの値が使われ(キーALPHA
)、後のファイルにしか存在しないキーは後のファイルの値が使われます(キーBETA
)。
require('dotenv').config(); console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env file ALPHA: AAA // index.js after reading .env file BETA: undefined require('dotenv').config({ path: '.env.next' }); console.log('==='); console.log('index.js', 'after reading .env.next file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next file BETA:', process.env.BETA); // index.js after reading .env.next file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env.next file ALPHA: AAA // index.js after reading .env.next file BETA: BBB
dotenvでoverrideオプションを使って後勝ちにする
後に読み込まれたファイルを優先するには、overrideオプションを使います。
overrideオプションにtrue
を設定することで重複しているキーは後に読み込まれたファイルの値が優先されて使われます。
以下の例だとMY_ENV_VALUE
が重複しており、後に読み込んだ.env.next
ファイルの値が使われています。
先のファイルの読み込みでoverrideオプションを使っても、後のファイル読み込みでoverrideオプションが使われていれば後勝ちとなります。
また、重複していないキーの値は上書きされません。
require('dotenv').config({ override: true }); console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env file ALPHA: AAA // index.js after reading .env file BETA: undefined require('dotenv').config({ path: '.env.next', override: true }); console.log('==='); console.log('index.js', 'after reading .env.next file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next file BETA:', process.env.BETA); // index.js after reading .env.next file MY_ENV_VALUE: DOTENV_NEXT_FILE // index.js after reading .env.next file ALPHA: AAA // index.js after reading .env.next file BETA: BBB
さらに、overrideオプションを使いつつ、最初に読み込んだ環境変数ファイルを、別のファイルを読み込んだあとにもう一度読み込むと、一番最後に読み込んだファイルの値が使われます。
require('dotenv').config({ override: true }); console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env file ALPHA: AAA // index.js after reading .env file BETA: undefined require('dotenv').config({ path: '.env.next', override: true }); console.log('==='); console.log('index.js', 'after reading .env.next file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next file BETA:', process.env.BETA); // index.js after reading .env.next file MY_ENV_VALUE: DOTENV_NEXT_FILE // index.js after reading .env.next file ALPHA: AAA // index.js after reading .env.next file BETA: BBB require('dotenv').config({ override: true }); console.log('==='); console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env file ALPHA: AAA // index.js after reading .env file BETA: BBB
Node.js CLIで指定された環境変数はデフォルトでdotenvよりも優先されるが、overrideオプションによって上書きされる
cli.js
ファイルを以下のように設定し、
console.log('index.js', 'before reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); // index.js before reading .env file MY_ENV_VALUE: CLI_VALUE require('dotenv').config(); console.log('==='); console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); // index.js after reading .env file MY_ENV_VALUE: CLI_VALUE require('dotenv').config({ path: '.env.next', override: true }); console.log('==='); console.log('index.js', 'after reading .env.next file using override=true MY_ENV_VALUE:', process.env.MY_ENV_VALUE); // index.js after reading .env.next file using override=true MY_ENV_VALUE: DOTENV_NEXT_FILE
環境変数を指定しながら、Node.js CLIでJSファイルを実行します。
$ MY_ENV_VALUE=CLI_VALUE node cli.js
dotenvで環境変数ファイルを読み込んでも重複しているキー(MY_ENV_VALUE
)はCLIで指定した環境変数の値が勝ちます。
ただし、overrideオプションを使って環境変数ファイルを読み込むとdotenvで読み込んだ環境変数ファイルの値が優先となります。
--env-file
オプションは後勝ち
env.js
を以下のようにし、
console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_NEXT_FILE // index.js after reading .env file ALPHA: AAA // index.js after reading .env file BETA: BBB
Node.js CLIで--env-file
オプションを複数指定して呼び出します。
$ node --env-file=.env --env-file=.env.next env.js
環境変数ファイルで重複しているキー(MY_ENV_VALUE
)は後に指定したファイルの値が利用され、重複していないキーはそれぞれの値が使われています。
--env-file
オプションで読み込んだ環境変数ファイルはデフォルトでdotenvよりも優先されるが、overrideオプションによって上書きされる
env2.js
ファイルを以下のようにし、
console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env file ALPHA: AAA // index.js after reading .env file BETA: undefined require('dotenv').config({ path: '.env.next'}); console.log('==='); console.log('index.js', 'after reading .env.next file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next file BETA:', process.env.BETA); // index.js after reading .env.next file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env.next file ALPHA: AAA // index.js after reading .env.next file BETA: BBB require('dotenv').config({ path: '.env.next2', override: true}); console.log('==='); console.log('index.js', 'after reading .env.next2 file with override MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next2 file with override ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next2 file with override BETA:', process.env.BETA); // index.js after reading .env.next file with override MY_ENV_VALUE: DOTENV_NEXT2_FILE // index.js after reading .env.next file with override ALPHA: AAA // index.js after reading .env.next file with override BETA: BBB
呼び出します。
$ node --env-file=.env env2.js
dotenvで環境変数ファイルを読み込んでも重複しているキー(MY_ENV_VALUE
)は--env-file
オプションで読み込んだ環境変数の値が勝ちます。
ただし、overrideオプションを使って環境変数ファイルを読み込むとdotenvで読み込んだ環境変数ファイルの値が優先となります。
Node.js CLIで環境変数を指定したときと同じく、dotenvはデフォルトでは負けです。
CLIで指定した環境変数 > --env-file
オプション > dotenv、ただしoverrideオプションによってdotenvが勝つ
これまでの指定方法3つを混ぜて使います。
env3.js
console.log('index.js', 'after reading .env file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env file BETA:', process.env.BETA); // index.js after reading .env file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env file ALPHA: CLI_VALUE // index.js after reading .env file BETA: undefined require('dotenv').config({ path: '.env.next'}); console.log('==='); console.log('index.js', 'after reading .env.next file MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next file ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next file BETA:', process.env.BETA); // index.js after reading .env.next file MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env.next file ALPHA: CLI_VALUE // index.js after reading .env.next file BETA: BBB require('dotenv').config({ path: '.env.next2', override: true}); console.log('==='); console.log('index.js', 'after reading .env.next2 file with override MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next2 file with override ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next2 file with override BETA:', process.env.BETA); // index.js after reading .env.next2 file with override MY_ENV_VALUE: DOTENV_NEXT2_FILE // index.js after reading .env.next2 file with override ALPHA: CLI_VALUE // index.js after reading .env.next2 file with override BETA: BBB require('dotenv').config({ path: '.env', override: true}); console.log('==='); console.log('index.js', 'after reading .env.next2 file with override MY_ENV_VALUE:', process.env.MY_ENV_VALUE); console.log('index.js', 'after reading .env.next2 file with override ALPHA:', process.env.ALPHA); console.log('index.js', 'after reading .env.next2 file with override BETA:', process.env.BETA); // index.js after reading .env.next2 file with override MY_ENV_VALUE: DOTENV_FILE // index.js after reading .env.next2 file with override ALPHA: AAA // index.js after reading .env.next2 file with override BETA: BBB
呼び出し
$ ALPHA=CLI_VALUE node --env-file=.env env3.js
CLIで環境変数として指定したキーALPHA
の値は、--env-file
オプションで読み込まれた値よりも優先されます。
--env-file
オプションで読み込まれた値は、dotenvで読み込んだMY_ENV_VALUE
の値よりも優先されます。
ただし、CLIで環境変数として指定した値も--env-file
オプションで読み込まれた値もdotenvのoverrideオプションを使うことで上書きできます。
おわりに
結論として強い順に並べると、
overrideオプションを使ったdotenvで後のファイル > overrideオプションを使ったdotenvで先のファイル > > CLIでの環境変数 > --env-file
オプション > dotenvで先のファイル > dotenvで後のファイル
となります。
紹介したdotenv、--env-file
オプションと環境変数という3つを全部同時に使うことはないでしょうが、dotenvと環境変数や--env-file
オプションと環境変数という組み合わせは発生するでしょう。
その際にお役に立ったら嬉しいです。
--env-file
オプションが将来的にStableとなったとき、dotenvモジュールの利用はなくなるのかもしれません。