Node.jsで環境変数を利用する方法

February 10, 2022
執筆者
レビュー担当者

Environment Variables in Node.js

この記事はTwilio Developer AdvocateのDominik Kundelがこちらで執筆した記事を日本語化したものになります。

環境変数は、Node.jsアプリケーションから秘匿性の高い情報にアクセスできる、すばらしい方法です。多くのクラウドホスト(Heroku、Azure、AWS、now.shなど)やNode.jsモジュールでは、環境変数を使用します。例を挙げると、ホストではサーバーがどのポートをリッスンすべきかを指定する PORT 変数を設定します。モジュールは NODE_ENV 変数の値によって異なる動作(ロギングなど)をするかもしれません。

この記事では、私がNode.jsで環境変数を扱う際に使用しているTipsやツールをいくつか紹介します。

基本

Node.jsは標準で環境変数へのアクセス方法を提供しています。Node.jsのプロセスが起動すると、グローバルオブジェクトのプロパティとしてenvオブジェクトを作成し、すでに存在している環境変数へアクセスできます。このオブジェクトを覗いてみたい場合は、nodeコマンドでNode.jsのREPL(対話モード)を起動し、次のコマンドを実行します。

console.log(process.env);

このコードは、現在のNode.jsプロセスが認識しているすべての環境変数を出力します。特定の変数にアクセスするには、オブジェクトのプロパティのようにアクセスできます。

console.log('The value of PORT is:', process.env.PORT);

上記のコードを実行しても自分自身のコンピュータではPORTの値がundefinedと表示されるでしょう。しかし、HerokuやAzureのようなクラウドホストでは、PORT変数を使用して、ルーティングが正しく動作するためにサーバがどのポートをリッスンすべきかを教えてくれます。したがって、クラウドホストでWebサーバをセットアップする際、最初にPORT変数を確認しそれを利用するか、またはデフォルト値を渡しリッスンするポートを決定します。

const app = require('http').createServer((req, res) => res.send('Ahoy!'));
const PORT = process.env.PORT || 3000; app.listen(PORT, () => {
   console.log(`Server is listening on port ${PORT}`);
});

ハイライトされた行は、環境変数のPORTが利用可能であればその値を変数の値として、そうでない場合は3000を待ち受けポートとして使用します。このコードをserver.jsのようなファイルに保存して実行してみてください。

node server.js

出力には、Server is listening on port 3000というメッセージが表示されます。次にCtrl+Cでサーバーを停止し、以下のコマンドで再起動します。

PORT=9999 node server.js

nodeコマンドの前にPORT=9999と環境変数としてPORTの値を指定しているため、今回はServer is listening on port 9999というメッセージが表示されます。

このようにprocess.envは通常のオブジェクトであるため、値の設定や上書きを簡単に行えます。

process.env.MY_VARIABLE = 'ahoy';

上のコードでは、MY_VARIABLEの値を設定、または上書きします。しかしこの値は現在のNode.jsプロセスの実行中に設定されたため、現在のプロセスとその子プロセスのみで利用可能である点に注意してください。全体として環境変数をオーバーライドすることは回避し、PORTの例で示したように環境変数を変数に代入する、あるいはその変数を初期化して利用すべきでしょう。

.env ファイルから変数をロード

複数の異なるNode.jsプロジェクトを1台のコンピュータで開発する場合、環境変数名が重複していることに気付くでしょう。例えば、別々のメッセージングアプリケーションで異なるTwilio Messaging Service SIDが必要な場合を考えてみましょう。両方ともTWILIO_MESSAGE_SERVICE_SIDという名前で定義されているというシナリオです。それぞれのプロジェクトで固有の設定を実現する方法として、.envファイルを使用できます。このファイルでは、さまざまな環境変数とその値を指定できます。

このファイルは機密性の高い情報が、ソースコントロールにチェックインチェックすべきではありません。そこで.env.gitignoreに追加しましょう。多くのTwilioデモアプリケーションで見かける.env.exampleファイルは、.envファイルにコピーし、値を設定することを想定しています。テンプレートファイルをプロジェクトの他のメンバーと共有する場合は、今回のようなファイルを用意します。

さて、このファイルから値を読み込むにはどうすればよいでしょうか。最も簡単な方法は、[dotenv](http://npm.im/dotenv)というnpmモジュールを使うことです。下記のコマンドでインストールできます。

npm install dotenv --save

その後、次の行をエントリファイルの一番上に追加します。

require('dotenv').config();

このコードは、プロジェクトのルートにある.envファイルを自動的に読み込み、値を初期化します。すでに設定されている変数は、スキップされます。しかし、本番環境では.envファイルを使用せず、それぞれのホストで直接値を設定してください。そのため、ロード文をif文で囲むことになります。

if (process.env.NODE_ENV !== 'production') {
    require('dotenv').config();
}

このコードでは、サーバーが本番モードで起動していない場合にのみ.envファイルを読み込みます。

実際に見てみましょう。さきほどのようにディレクトリにdotenvをインストールします。同じディレクトリにdotenv-example.jsファイルを作成し、以下の行を配置します。

console.log('No value for FOO yet:', process.env.FOO);
if (process.env.NODE_ENV !== 'production') {
   require('dotenv').config();
}
console.log('Now the value for FOO is:', process.env.FOO);

その後、同じディレクトリに.envファイルを作成し、次の値を設定します。

FOO=bar

スクリプトを実行しましょう。

node dotenv-example.js

出力は以下のようになります。

No value for FOO yet: undefined
Now the value for FOO is: bar

ご覧のように、値はdotenvを使用して読み込まれ、定義されます。NODE_ENVproductionに設定して同じコマンドを再実行すると、未定義のままであることが分かります。

NODE_ENV=production node dotenv-example.js

この場合、下記のように出力されます。

No value for FOO yet: undefined
Now the value for FOO is: undefined

実際のコードを変更しない場合は、Nodeの-r引数を使用してスクリプトを実行する際にdotenvをロードできます。dotenv-example.jsファイルを変更します。

console.log('The value for FOO is:', process.env.FOO);

では、まず普通に実行してみましょう。

node dotenv-example.js

このスクリプトは、FOOの現在の値がundefined定義であることを表示します。次に、dotenvを必要とするフラグを立てた上で実行します。

node -r dotenv/config dotenv-example.js

これで.envファイルが読み込まれFOOの値がbarに設定されました。

dotenvについての詳細は、ドキュメントを確認してみてください。

.envファイルを読み込む別の方法

dotenvはすばらしいのですが、開発中に個人的に気になることがありました。それは、既存の環境変数を上書きせず、またそれを強制もできないことです。

このことを解決し環境変数の読み込みをより便利にするため、dotenvをベースにした独自のモジュールを作成することにしました。その結果がnode-env-runまたはnodenvです。これは、dotenvを使いenvファイルをロード、そして値を初期化後、スクリプトを実行するコマンドラインツールです。

グローバルにインストールできますが、開発とローカル実行を目的として使用することをお勧めします。このツールは下記のコマンドでインストールします。

npm install node-env-run --save-dev

その後、nodenv-example.jsというファイルを作成し、その中に以下のコードを配置します。

console.log('The value for FOO is:', process.env.FOO);

ご覧のように、ここでは何も必要ありません。ただのアプリケーションロジックです。まずはnodeコマンドを使って実行します。

node nodenv-example.js

この実行されたコードはThe value for FOO is: undefinedと出力されます。次にnode-env-runを使って実行します。

node_modules/.bin/nodenv nodenv-example.js

.envファイルを読み込んでいるのでThe value for FOO is: barとなります。

node-env-runは既存の値を上書きできます。最初に上書きをしない場合の動作を確認します。

FOO=foo node_modules/.bin/nodenv nodenv-example.js

出力はThe value for FOO is: fooとなります。次にforceモードを有効にすると、既存の値を上書きします。

FOO=foo node_modules/.bin/nodenv --force nodenv.js

FOOが上書きされ、The value for FOO is: barと出力されます。

このツールを定期的に使う場合は、npmスクリプトに設定することをお勧めします。package.jsonに以下のように追加します。

{ 
    "name": "twilio-blog", 
    "version": "1.0.0", 
    "description": "", 
    "main": "nodenv-example.js", 
    "scripts": { 
        "start": "node .", 
        "start:dev": "nodenv -f ." 
    }, 
    "author": "", 
    "license": "ISC", 
    "devDependencies": { 
        "node-env-run": "^2.0.1" } 
    }

このようにして、単純に実行するだけです。

npm run start:dev

node-env-runについての詳細は、ドキュメントを確認してください。

環境変数とnpmスクリプト

npmスクリプトでNode.jsアプリケーションを実行する場合、環境変数の値を確認しておくと便利なシナリオがあります。例えば、開発環境ではnode-env-runを使用し、本番環境ではnodeを使用したい場合などです。これを非常に簡単にしてくれるツールがif-envです。

npm install if-env --save

本番環境でも必要になるので、dev dependencyとしてインストールしないように注意してください。

インストール後、package.jsonファイルのnpmスクリプトを修正します。

"scripts": { 
    "start": "if-env NODE_ENV=production ?? npm run start:prod || npm run start:dev",
    "start:dev": "nodenv -f .", 
    "start:prod": "node ." 
}

このスクリプトはNODE_ENVproductionと設定されていれば、npm run start:prod、つまりnode .を実行します。そうでなければ、npm run start:dev、つまりnodenv -fを実行します。

# should output "The value of FOO is: bar" 
npm start 

# should output "The value of FOO is: undefined"
NODE_ENV=production npm start

 

if-envについての詳細は、ドキュメントを参照してください。

デバッグ

動作が想定通りに動かない場合や、モジュールが本来の動作をしていない可能性があります。その時こそデバッグを実施します。。私がとても役に立ったことは、環境変数DEBUGを使って、多くのモジュールの詳細なログを受け取ることでした。例えば、基本的なexpressサーバーを次のように設定します。

const app = require('express')(); 
const bodyParser = require('body-parser'); 
app.use(bodyParser.json()); 

// for parsing application/json 
app.use(bodyParser.urlencoded({ extended: true })); 

// for parsing application/x-www-form-urlencoded
app.post('/', (req, res, next) => {
    console.log(req); 
});
app.listen(3000);

そして、DEBUG変数を*に設定して起動します。

DEBUG=* node server.js

以下のような大規模なログの束を受け取ることになります。

debug log

この背後にある「魔法」は、debugと呼ばれる軽量モジュールで、その使用方法は非常に簡単です。これを使いたいときは、「名前空間」を初期化します。その後、その名前空間に出力されたログを取得できます。特定の出力を見たい場合は、DEBUG変数で出力したい名前空間を設定します。例えば、express routerのすべての出力を見る場合は、適切なワイルドカードを使ってDEBUGを設定します。

DEBUG=express:router* node server.js

自分が作成する独自のモジュールでdebugを使用する場合、まずインストールする必要があります。

npm install debug --save

そして、以下のように利用します。

const debug = require('debug')('myApp:someComponent');
debug('Here is a pretty object %o', {
    someObject: true });

debugについての詳細は、ドキュメントを確認してください。

すべての環境変数を使用

今回紹介した内容は、環境変数やツールを使ってできることのすべてではなく、私が最もよく使っているものだけです。ほかにも普段使っているすばらしいツールがあれば、ぜひ教えてください。

Node.jsの環境変数について少し分かったところで、Twilio Node.jsクイックスタートを試してみましょう。