2023.3.3
環境変数、process.env、.envファイルでできること
そもそも環境変数とは
環境変数(かんきょうへんすう、英語: environment variable)はオペレーティングシステム (OS) が提供するデータ共有機能の一つ。OS上で動作するタスク(プロセス)がデータを共有するための仕組みである。
wikipedia より
ということなので、OS上で動作するプロセスが参照できるグローバル変数ということになります。
私のOSはmacOSですが、まずは環境変数になにが定義されているのかterminalでコマンドを実行して確認してみます。
bash_____terminal_____$ ENV
MallocNanoZone=0
USER=kobayashi
COMMAND_MODE=unix2003
... // 40行くらい
定義された環境変数の一覧が表示されました。
これらはOS上で動作するタスクから参照可能な変数ということになります。
process.envとは
以降は実行環境はNodeでJavaScriptのアプリケーションとして話を進めていきます。
process.envとは上記の環境変数がバインドされたオブジェクトで、実行環境がNodeであればprocess.envに環境変数がバインドされています。
一番シンプルな例で確認してみます。
JavaScript_____index.js_____console.log(process.env);
上記のファイルを作成し、次のコマンドを実行します。
bash_____terminal_____$ node index.js
すると、最初にterminalで ENV
コマンドを実行したときと同じ結果が得られます。
これで実行環境がNodeであればprocess.envから環境変数が参照できることがわかりました。
.envとは
意外と何気なく使っている人が多い .env
ですが .env
で定義した環境変数がなぜprocess.envから参照できるのか知っているでしょうか。
昨今のモダンな技術スタック(React.js、Next.jsなど)を使っていると .env
というファイル名でプロジェクトのルートディレクトリに配置したら勝手にprocess.envから参照できるようになるんだ!と思っているかもしれませんが、そうではありません。
試しに先ほど index.js
を作ったディレクトリで .env
というファイルを以下のように作ってみます。
text_____.env_____MY_VALUE=my_value
そして index.js
を次のように編集します。
JavaScript_____index.js_____console.log(process.env.MY_VALUE);
これで再度以下のコマンドを実行すると undefined
となります。
bash_____terminal_____$ node index.js
undefined
.envはdotenvというライブラリでバインドされる
.env
はなにか手を加えないとprocess.envにバインドされないことがわかりましたが、これをバインドしてくれるのが dotenv
というライブラリです。
やっていることはシンプルで main.js
も100行ちょっとしかないライブラリなので良かったら読んでみるとより理解が深まるかと思います。
早速試すために、以下のようにコマンドを実行します。
bash_____terminal_____$ npm init -y
$ npm i dotenv
次に index.js
を以下のように編集します。
JavaScript_____index.js_____console.log('MY_VALUE:', process.env.MY_VALUE);
const dotenv = require('dotenv').config();
console.log('\n= = = dotenv is loaded. = = =\n');
console.log('MY_VALUE:', process.env.MY_VALUE);
そして以下のコマンドを実行します。
bash_____terminal_____$ node index.js
MY_VALUE: undefined
= = = dotenv is loaded. = = =
MY_VALUE: my_value
上記より、dotenvを利用して .env
で定義した MY_VALUE
という環境変数がprocess.envにバインドされたことがわかりました。npx create-next-app
でNext.jsアプリケーションを作成すると、node_modules配下にdotenvというディレクトリが見つかりますが、モダンな技術スタックなプロジェクトならまず見つかるはずです。
.envで環境変数を参照する環境変数を定義する
次のような環境変数定義を見たことがないでしょうか。
text_____.env_____MY_VALUE=my_value
MY_MESSAGE="MY_VALUE is ${MY_VALUE}"
いかにも MY_MESSAGE
の中で MY_VALUE
が展開されそうです。
上記のように .env
を更新したあと、次のように index.js
を更新します。
JavaScript_____index.js_____console.log('MY_VALUE:', process.env.MY_VALUE);
console.log('MY_MESSAGE:', process.env.MY_MESSAGE);
const dotenv = require('dotenv').config();
console.log('\n= = = dotenv is loaded. = = =\n');
console.log('MY_VALUE:', process.env.MY_VALUE);
console.log('MY_MESSAGE:', process.env.MY_MESSAGE);
これで実行してみます。
bash_____terminal_____$ node index.js
MY_VALUE: undefined
MY_MESSAGE: undefined
= = = dotenv is loaded. = = =
MY_VALUE: my_value
MY_MESSAGE: MY_VALUE is ${MY_VALUE}
MY_VALUE
は展開されずにそのままテキストとして参照されました。
これが展開されるようにしてくれているのがdotenv-expandというライブラリです。
dotenv-expandをインストールして確認します。
bash_____terminal_____$ npm i dotenv-expand
次に index.js
を以下のように更新します。
JavaScript_____index.js_____console.log('MY_VALUE:', process.env.MY_VALUE);
console.log('MY_MESSAGE:', process.env.MY_MESSAGE);
const dotenv = require('dotenv').config();
console.log('\n= = = dotenv is loaded. = = =\n');
console.log('MY_VALUE:', process.env.MY_VALUE);
console.log('MY_MESSAGE:', process.env.MY_MESSAGE);
require('dotenv-expand').expand(dotenv);
console.log('\n= = = dotenv-expand is loaded. = = =\n');
console.log('MY_VALUE:', process.env.MY_VALUE);
console.log('MY_MESSAGE:', process.env.MY_MESSAGE);
これで再度実行してみます。
bash_____terminal_____$ node index.js
MY_VALUE: undefined
MY_MESSAGE: undefined
= = = dotenv is loaded. = = =
MY_VALUE: my_value
MY_MESSAGE: MY_VALUE is ${MY_VALUE}
= = = dotenv-expand is loaded. = = =
MY_VALUE: my_value
MY_MESSAGE: MY_VALUE is my_value
dotenv-expandで MY_MESSAGE
の中の ${MY_VALUE}
は MY_VALUE
が参照されて MY_MESSAGE
の中で展開されました。
まとめ
モダンな開発環境で当然のように .env
を利用していたのにこのあたりを知らなかったという人の役に立てば幸いです。
このあたりがわかっていると、例えばAWS Secrets Managerで定義した値をECSのタスクの環境変数として参照できるように定義し、コンテナ内のアプリケーションからAWS Secrets Managerの値を参照するようなことができたりするので、基本を抑えておくと役に立つのではないかなと思います。