Skip to content
Go back

dotenv-flow を試してみた - 環境ごとに .env ファイルを使い分ける

はと🐤テック

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* ファイルのサポートを追加したライブラリです。

主な特徴

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_USERDATABASE_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

以下のようにマージされていることが分かります:

特に注目したいのは LOG_LEVEL です。.env.development では debug に設定していましたが、.env.development.localtrace で上書きされています。これは優先順位がきちんと機能している証拠だと思います。

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_USERlocal_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_USERdefault_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

コードに何も書かなくても、きちんと環境変数が読み込まれました。これは便利だと思います。

優先順位のまとめ

実際に試してみて分かった読み込み順序(優先度が低い順):

  1. .env - ベースとなる設定(最も優先度が低い)
  2. .env.local - ローカル環境の設定(ただし NODE_ENV=test の場合は読み込まれない)
  3. .env.${NODE_ENV} - 環境別の設定(例: .env.development
  4. .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 は、シンプルで使いやすく、よく考えられたライブラリだと感じました。特に以下の点が印象的でした:

どんな場合に使えそうか

私の経験から、以下のような場合に特に向いていると思います:

逆に、環境変数が少ない場合や、既に別の設定管理の仕組みがある場合は、必ずしも必要ないかもしれません。

個人的な感想

実際に動かして試してみると、ドキュメントを読むだけでは分からなかった細かい挙動(例: テスト環境で .env.local が読み込まれないこと)が体感できて、理解が深まりました。

環境変数の管理方法に悩んでいる方や、dotenv から一歩進んだ設定管理を考えている方には、試してみる価値があると思います。

参考リンク


dotenv-flow を実際に試してみて、環境ごとの設定管理がシンプルに実現できることが分かりました。特に優先順位の仕組みが明確で、期待通りに動作する点が良いと思います。

環境別の設定管理を検討している方は、ぜひ一度試してみてはいかがでしょうか。

この記事が、同じようにこのライブラリに興味を持った方の参考になれば幸いです。


Share this post on:

Previous Post
dotenv-safe を試してみた - .env.example で環境変数の設定漏れを防ぐ
Next Post
react-router-auto-routes を試してみた - フォルダベースの自動ルーティング