Disclaimer: This article was written by AI and may contain inaccuracies or errors.
Table of contents
Open Table of contents
はじめに
Node.js アプリケーションで環境変数を管理する際、dotenv を使うのが一般的ですが、開発環境・テスト環境・本番環境でそれぞれ異なる設定を使いたい場合、環境ごとに .env ファイルを分けたくなることがあります。
そんなときに便利そうなのが dotenv-flow です。このライブラリは NODE_ENV に応じて .env.development、.env.production などを自動的に読み込んでくれます。
今回、このライブラリを実際に試してみたので、その体験をレポートします。
dotenv-flow とは
dotenv-flow は、dotenv を拡張して NODE_ENV に応じた複数の .env* ファイルのサポートを追加したライブラリです。
主な特徴
- 環境別の .env ファイルをサポート -
.env.development、.env.test、.env.productionなど - ローカルオーバーライド -
.env.local、.env.development.localなどでローカル環境の設定を上書き - 明確な優先順位 - 複数のファイルを決まった順序で読み込み、マージ
- dotenv との互換性 - dotenv の API を踏襲しているため、移行が容易
Ruby on Rails の dotenv-rails や Create React App の環境変数管理に影響を受けているそうで、それらに慣れている方には馴染みやすい設計だと思います。
セットアップ
まずは npm でインストールします。
npm install dotenv-flow
使い方は dotenv と同様、アプリケーションの最初の方で以下のように呼び出します。
require("dotenv-flow").config();
TypeScript や ES Modules の場合は:
import dotenvFlow from "dotenv-flow";
dotenvFlow.config();
実際に試してみる
実際にどのように動作するか確かめるため、シンプルなデモアプリケーションを作ってみました。
1. .env ファイルを用意
まず、以下のような構成で .env* ファイルを作成しました。
# .env - ベースとなる設定
APP_NAME=MyAwesomeApp
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=default_user
DATABASE_PASS=
LOG_LEVEL=info
# .env.local - ローカル開発用のオーバーライド
DATABASE_USER=local_dev
DATABASE_PASS=secret123
# .env.development - 開発環境の設定
DATABASE_HOST=dev-db.example.com
LOG_LEVEL=debug
API_ENDPOINT=https://api-dev.example.com
# .env.production - 本番環境の設定
DATABASE_HOST=prod-db.example.com
LOG_LEVEL=error
API_ENDPOINT=https://api.example.com
ENABLE_MONITORING=true
# .env.development.local - 個人的な開発環境の調整
LOG_LEVEL=trace
ENABLE_DEBUG_MODE=true
2. テストアプリケーションを作成
環境変数を表示するシンプルなスクリプトを作りました。
// index.js
require("dotenv-flow").config();
console.log("========================================");
console.log("dotenv-flow Demo Application");
console.log("========================================");
console.log("");
console.log("Current NODE_ENV:", process.env.NODE_ENV || "(not set)");
console.log("");
console.log("Loaded Environment Variables:");
console.log("----------------------------------------");
console.log("APP_NAME:", process.env.APP_NAME);
console.log("DATABASE_HOST:", process.env.DATABASE_HOST);
console.log("DATABASE_PORT:", process.env.DATABASE_PORT);
console.log("DATABASE_USER:", process.env.DATABASE_USER);
console.log("DATABASE_PASS:", process.env.DATABASE_PASS ? "******" : "(empty)");
console.log("LOG_LEVEL:", process.env.LOG_LEVEL);
console.log("API_ENDPOINT:", process.env.API_ENDPOINT);
console.log("ENABLE_MONITORING:", process.env.ENABLE_MONITORING);
console.log("ENABLE_DEBUG_MODE:", process.env.ENABLE_DEBUG_MODE);
console.log("========================================");
3. 異なる環境で実行してみる
NODE_ENV を指定しない場合
$ node index.js
Current NODE_ENV: (not set)
Loaded Environment Variables:
----------------------------------------
APP_NAME: MyAwesomeApp
DATABASE_HOST: localhost
DATABASE_PORT: 5432
DATABASE_USER: local_dev
DATABASE_PASS: ******
LOG_LEVEL: info
API_ENDPOINT: undefined
ENABLE_MONITORING: undefined
ENABLE_DEBUG_MODE: undefined
この場合、.env と .env.local だけが読み込まれました。DATABASE_USER と DATABASE_PASS は .env.local の値で上書きされていることが確認できます。
NODE_ENV=development の場合
$ NODE_ENV=development node index.js
Current NODE_ENV: development
Loaded Environment Variables:
----------------------------------------
APP_NAME: MyAwesomeApp
DATABASE_HOST: dev-db.example.com
DATABASE_PORT: 5432
DATABASE_USER: local_dev
DATABASE_PASS: ******
LOG_LEVEL: trace
API_ENDPOINT: https://api-dev.example.com
ENABLE_MONITORING: undefined
ENABLE_DEBUG_MODE: true
以下のようにマージされていることが分かります:
APP_NAME、DATABASE_PORTは.envからDATABASE_USER、DATABASE_PASSは.env.localからDATABASE_HOST、API_ENDPOINTは.env.developmentからLOG_LEVEL、ENABLE_DEBUG_MODEは.env.development.localから(最も優先度が高い)
特に注目したいのは LOG_LEVEL です。.env.development では debug に設定していましたが、.env.development.local の trace で上書きされています。これは優先順位がきちんと機能している証拠だと思います。
NODE_ENV=production の場合
$ NODE_ENV=production node index.js
Current NODE_ENV: production
Loaded Environment Variables:
----------------------------------------
APP_NAME: MyAwesomeApp
DATABASE_HOST: prod-db.example.com
DATABASE_PORT: 5432
DATABASE_USER: local_dev
DATABASE_PASS: ******
LOG_LEVEL: error
API_ENDPOINT: https://api.example.com
ENABLE_MONITORING: true
ENABLE_DEBUG_MODE: undefined
本番環境用の設定が読み込まれました。なお、.env.local も読み込まれています(DATABASE_USER が local_dev になっている)。
NODE_ENV=test の場合
$ NODE_ENV=test node index.js
Current NODE_ENV: test
Loaded Environment Variables:
----------------------------------------
APP_NAME: MyAwesomeApp
DATABASE_HOST: localhost
DATABASE_PORT: 5432
DATABASE_USER: default_user
DATABASE_PASS: (empty)
LOG_LEVEL: info
API_ENDPOINT: undefined
ENABLE_MONITORING: undefined
ENABLE_DEBUG_MODE: undefined
テスト環境では .env.local が読み込まれていないことに気づきました。DATABASE_USER が default_user(.env の値)になっています。
これは意図的な設計で、テスト環境では個人的なローカル設定に影響されず、一貫した結果が得られるようになっているそうです。なるほど、と思いました。
プリロード機能も試してみる
Node.js の -r フラグを使うと、コードに require() を書かなくても環境変数を読み込めます。
// app-without-require.js
// dotenv-flow を require していない
console.log("NODE_ENV:", process.env.NODE_ENV || "(not set)");
console.log("APP_NAME:", process.env.APP_NAME);
console.log("DATABASE_HOST:", process.env.DATABASE_HOST);
console.log("LOG_LEVEL:", process.env.LOG_LEVEL);
$ NODE_ENV=development node -r dotenv-flow/config app-without-require.js
NODE_ENV: development
APP_NAME: MyAwesomeApp
DATABASE_HOST: dev-db.example.com
LOG_LEVEL: trace
コードに何も書かなくても、きちんと環境変数が読み込まれました。これは便利だと思います。
優先順位のまとめ
実際に試してみて分かった読み込み順序(優先度が低い順):
.env- ベースとなる設定(最も優先度が低い).env.local- ローカル環境の設定(ただしNODE_ENV=testの場合は読み込まれない).env.${NODE_ENV}- 環境別の設定(例:.env.development).env.${NODE_ENV}.local- 環境別のローカル設定(最も優先度が高い)
また、既にシェルで設定されている環境変数は、.env* ファイルの値よりもさらに優先されます。
良かった点
1. シンプルで分かりやすい
基本的な使い方は dotenv と同じなので、学習コストがほとんどありませんでした。require('dotenv-flow').config() を呼ぶだけで、あとは .env* ファイルを配置すれば動作します。
2. 優先順位が明確
どのファイルがどの順番で読み込まれるか、README に明確に書かれています。実際に試してみても、ドキュメント通りに動作しました。この透明性は信頼感につながると思います。
3. テスト環境での配慮
NODE_ENV=test のときに .env.local を読み込まないという設計は、テストの再現性を保つために理にかなっていると感じました。
4. バージョン管理との統合が考えられている
README では、どのファイルをバージョン管理に含めるべきか(tracked)、どのファイルを .gitignore に入れるべきか(ignored)が明示されています。
# local .env* files
.env.local
.env.*.local
このように、セキュリティとチーム開発の両面が考慮されているように思います。
5. プリロード機能
-r dotenv-flow/config でコードを変更せずに使える点は、既存のアプリケーションへの導入を試す際に便利だと感じました。
6. 充実したドキュメント
README が非常に詳しく書かれており、使い方だけでなく、設計思想や背景にある考え方まで説明されています。初めて使う際も、安心して試すことができました。
気づいた点
1. ファイルが増える
環境が増えると、.env* ファイルの数も増えていきます。開発・テスト・ステージング・本番、さらにそれぞれの .local バージョンを考えると、かなりの数になります。
これ自体は dotenv-flow の問題ではなく、環境ごとに設定を分けるという方針の自然な結果だと思います。ただ、プロジェクトによっては、別の方法(環境変数を外部の設定管理サービスで管理するなど)の方が適している場合もあるかもしれません。
2. .env.defaults というオプション
dotenv-flow は .env の代わりに .env.defaults を使うこともサポートしています。これは、dotenv から移行する際に、既存の .env(ローカル設定として使っている)を残しつつ、新しいデフォルト値を .env.defaults に書ける、という用途のようです。
私は今回試しませんでしたが、移行時には便利かもしれません。
3. 環境変数の数が多い場合
デモでは少数の環境変数しか使いませんでしたが、実際のアプリケーションでは数十〜数百の環境変数を扱うこともあると思います。その場合、どの変数がどのファイルで定義されているかを把握するのが難しくなるかもしれません。
ただ、これは dotenv-flow 固有の問題ではなく、環境変数を多用する場合の一般的な課題だと思います。
まとめ
私が感じたこと
dotenv-flow は、シンプルで使いやすく、よく考えられたライブラリだと感じました。特に以下の点が印象的でした:
- dotenv との互換性 - 既存の知識がそのまま活かせる
- 明確な設計思想 - ドキュメントから、なぜこういう設計なのかが伝わってくる
- テストへの配慮 -
NODE_ENV=testでの挙動が合理的
どんな場合に使えそうか
私の経験から、以下のような場合に特に向いていると思います:
- 開発・テスト・本番で異なる設定を使いたい - これが dotenv-flow の主な用途
- チーム開発で、共通の設定と個人の設定を分けたい -
.localファイルが便利 - dotenv を既に使っていて、環境別設定を追加したい - 移行が容易
逆に、環境変数が少ない場合や、既に別の設定管理の仕組みがある場合は、必ずしも必要ないかもしれません。
個人的な感想
実際に動かして試してみると、ドキュメントを読むだけでは分からなかった細かい挙動(例: テスト環境で .env.local が読み込まれないこと)が体感できて、理解が深まりました。
環境変数の管理方法に悩んでいる方や、dotenv から一歩進んだ設定管理を考えている方には、試してみる価値があると思います。
参考リンク
dotenv-flow を実際に試してみて、環境ごとの設定管理がシンプルに実現できることが分かりました。特に優先順位の仕組みが明確で、期待通りに動作する点が良いと思います。
環境別の設定管理を検討している方は、ぜひ一度試してみてはいかがでしょうか。
この記事が、同じようにこのライブラリに興味を持った方の参考になれば幸いです。