dotenvとcross-envで環境変数を設定して開発環境の処理を切り替える
開発用と公開用で読み込むデータを変更したい、Gitで同じ開発環境を共有しながら一部のデータは作業者のPCで設定できるようにしたい。
要件が複雑な案件では、開発環境もより柔軟性のある設定にしておきたいことがあります。
たとえば、https
環境が必須の機能を検証するためにbrowser-syncの設定が必要な場合です。各自のPCで証明書の発行が必要なので、Gitで共有することができません。
Google Maps APIやデータベースと連携するAPIを開発用と公開用で変更する場合もあります。切り替えるたびにソースコードを書き換えていると、本来不必要なコミット履歴が増え、開発用のAPIのまま本番環境に公開してしまったなんてことも起こってしまうかもしれません。
今回は柔軟性のある開発環境にするために便利な、環境変数とenv(エンブ)を紹介します。
環境変数とenv(エンブ)とは
環境変数とは、OSやアプリケーション開発環境などの実行環境が持っている、自身の状態を表す変数です。
envとは、環境変数を参照したり編集する仕組みのことです。環境変数を操作するLinuxのenv
コマンドから来ています。
GulpやwebpackはNode.jsで動いてます。この実行環境の環境変数はprocess.env
経由で参照するのが一般的です。
コマンドを実行して環境変数を設定してもいいのですが、フロントエンドの開発環境ならdotenvとcross-envを使うのが一般的です。
dotenvとは
開発環境のルートに環境変数を設定した.envファイルを作成すると、process.env
経由で参照してくれるパッケージです。元々はRuby発のOSSですが、今回はNode.js版を使います。
npm install --save-dev dotenv
環境変数は以下のように設定します。
4つのルールを覚えておきましょう(それ以外のルールはこちらを参照)。
#
から始める行はコメントになりますKEY1=VALUE1
は{KEY1: 'VALUE1'}
として解釈されますKEY1=
は{KEY1: ''}
として解釈されます- 空行はスキップされます
#これはコメントです
KEY1=VALUE1
#KEY2=VALUE2
KEY3=
gulpfile.jsとwebpack.config.jsからは、require('dotenv').config();
とした後にprocess.env
経由で結果を受け取れます。
たとえば、KEY1=VALUE1
はprocess.env.KEY1
から'VALUE1'
が受け取れます。
.envは外部に漏れてはいけないデータを含むことが多いため、.gitignoreに追加してコミットされないようにしておきます。
.env
cross-envとは
WindowsでNODE_ENV=production
のように環境変数を設定すると、コマンドプロンプトが停止してしまいます。cross-envはそういったプラットフォームの差異を吸収してくれるパッケージです。Nuxt.jsやNext.jsでも公式に紹介されています。
dotenvと同じくnpmからインストールします。
npm install --save-dev cross-env
環境変数の設定は、npmスクリプトでGulpタスクを実行する箇所に追加します。
"scripts": {
"start": "cross-env NODE_ENV=development gulp",
"release": "cross-env NODE_ENV=production gulp build",
},
上記の例では、npm start
でprocess.env.NODE_ENV
にdevelopment
を渡して開発用の処理を、npm run release
でproduction
を渡して公開用の処理を実行しています。
NODE_ENV
は、実行環境が本番環境なのかを判定する時に使われる環境変数です。production
で本番、development
で開発、test
でテストがよく使われます。
詳しい説明は後述しますが、process.env.NODE_ENV
で受け取った値で条件分岐をさせて、専用の設定ファイル(グローバル定数)を読み込むこともできます。
dotenvのユースケース
browser-syncのhttps
オプションに証明書のパスを指定する方法を紹介します。
browser-syncのhttps
オプションに証明書のパスを指定する
コミット対象から外した.envの内容をGitで共有するために、開発環境のルートに.env.exampleを作成して、全体共通の設定を書きます。
今回は、以下のように設定してcp .env.example .env
を実行します。
#browser-syncで`https`オプションを使用する場合にコメントを解除します
#HTTPS_KEY=/Users/<ユーザー名>/<IPアドレス>-key.pem
#HTTPS_CERT=/Users/<ユーザー名>/<IPアドレス>.pem
.env.exampleが.envとして複製されるのが確認できます。
このように、.env.exampleを経由して.envの全体設定を共有するのが一般的です。必要に応じて.envの設定を上書きしたり追加します。
HTTPSに必要な証明書はmkcertで作成します(もちろん他の方法でも大丈夫です)。今回はMacのhomebrewを使っています。
homebrewからmkcertをインストールします。
brew install mkcert
Firefoxで確認する場合はnssもインストールする必要があります。
brew install nss
ローカルに認証局を作成します。
mkcert -install
証明書を作成します。
「システム環境設定」の「ネットワーク」を開き、「詳細」ボタンをクリックします。「TCP/IP」のタブの「IPv4アドレス:」をコピーして、以下のように実行します。
mkcert <IPアドレス>
/Users/<ユーザー名>/<IPアドレス>-key.pemと/Users/<ユーザー名>/<IPアドレス>.pemが生成されます。
.envのコメント#
を削除して、HTTPS_KEY
とHTTPS_CERT
に証明書へのパスを設定すれば完了です。
#browser-syncで`https`オプションを使用する場合にコメントを解除します
HTTPS_KEY=/Users/<ユーザー名>/<IPアドレス>-key.pem
HTTPS_CERT=/Users/<ユーザー名>/<IPアドレス>.pem
gulpfile.jsから.envに設定した証明書のデータを取得・設定していきます。
dotenvを通して.envのデータを取得します。.envが見つからない場合は無視されるので、条件分岐させるく必要はありません。
require('dotenv').config();
browser-syncのhttps
オプションに、証明書のデータが取得できたら設定、取得できなかったらfalse
を渡す(http
を使う)ように設定します。
function serve(done) {
const httpsOption =
process.env.HTTPS_KEY !== undefined
? { key: process.env.HTTPS_KEY, cert: process.env.HTTPS_CERT }
: false;
browserSync({
https: httpsOption,
});
done();
}
serve
タスクを実行すると、https
環境で表示されるのが確認できます。
cross-envのユースケース
webpack.config.jsのmode
オプションを切り替える処理と、JSファイルにグローバル定数を渡す処理の2つを紹介します。
webpack.config.jsのmode
オプションを切り替える
webpack.config.jsのmode
オプションは、開発と公開で処理を最適化してくれます。たとえば、production
時にはJSのMinify(圧縮)やTree Shaking(使用していないコードの除去)をします。
mode:
にはdevelopment
・production
・none
のいずれかを指定します。
const environment = process.env.NODE_ENV;
module.exports = {
mode: environment,
};
上記のように設定すると、npm scriptsから以下のように結果を受け取れるので、実行時のスクリプトで処理を切り替えられます。
cross-env NODE_ENV=development
=>development
cross-env NODE_ENV=production
=>production
JSファイルにグローバル定数を渡す
webpack.config.jsからコンパイル対象のJSファイルにグローバル定数を渡す場合はDefinePluginを使います。
const webpack = require('webpack');
const environment = process.env.NODE_ENV;
const environmentConfig = require(`./config/${environment}.js`); // eslint-disable-line
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env': JSON.stringify(environmentConfig),
}),
],
};
上記では、./config/${environment}.js
の箇所で、読み込むJSファイルを切り替えています。たとえば、npmスクリプトでcross-env NODE_ENV=production
を渡していたら、./config/production.js
をインポートします。参照はprocess.env.KEY
のようにします。
DefinePluginでコンパイル対象のJSファイルにも渡しているので、同じように参照できます。
config/development.jsとconfig/production.jsを作成して、以下のようにデータを設定します。
// config/development.js
module.exports = {
API_BASE_URL: 'dev.example.com',
}
// config/production.js
module.exports = {
API_BASE_URL: 'prod.example.com',
}
webpack.config.jsやコンパイル対象のJSファイルでconsole.log(process.env.API_BASE_URL);
のように実行すると、dev.example.com
やprod.example.com
が出力されるのが確認できます。
まとめ
- 環境変数は実行している開発環境自身の変数
- envは環境変数を設定したり参照すること、またはそのパッケージ
- dotenvはコミット対象外の.envで各自が設定を変更したり、外部に漏らしてはいけないデータを扱う場合に使う
- cross-envは開発用と公開用でデータや処理を切り替えたい場合に使う
envを使えるようになると、開発環境の柔軟性が大きく上がります。
使ったことがない人は、ぜひ試してみてください。
gulp-ifを使って、開発時の処理を簡素化すると、コンパイル時間の短縮もできるのでオススメです。