webpackなどモジュールバンドラーを使ってビルドする際に本番ビルドと開発ビルドで情報を分けたいときがあると思います。
APIのURLとかは一番わかりやすい例ですがそういった場合にどうするか?
webpackであればDefine Pluginなどを使ってJSなどで定数として利用することができます。
しかし、そこでもソースコード上にif (process.NODE_ENV === 'production'){}などと書いたり、それを利用してJS側でif (FOOBAR) {}などするという手もありますが、それだとソースコードが無駄に肥大化していきますし、無駄な情報が多くなってしまいます。
そこで、node-configというNode.js用のライブラリを利用することで先程のif文のような無駄な分岐などを抑えることができます。
node-configのインストール
npmやyarnでインストールできます。
# ローカルにインストールする場合 install --save-devの略
$ npm i -D config
node-configの主なメリット
node-configはファイルで情報をまとめられます。
NODE_ENV=developmentであればdevelopment.EXT、NODE_ENV=productionであればproduction.EXTといった具合です。
利用可能なファイル拡張子としては下記があります。
.json… JSON.json5… コメント付きJSON.hjson… Human JSON.yamlor.yml… YAML.js… JavaScript.coffee… CoffeeScript.cson… CoffeeScriptのオブジェクト形式.properties… ドット記法形式.toml… TOML ミニマル記法.ts… TypeScript.xml… XML。利用する場合はx2jsライブラリが必要
また、default.EXTは存在すれば必ず読み込むので環境変数に関係ない共通情報を作成することができます。
環境変数ごとに情報をまとめられるというのは大きなメリットだと考えます。
node-configの構成
node-configはプロジェクト直下にconfig/というディレクトリを作成してファイルを置く必要があります。
project/
└ config/
├ default.EXT … 環境変数に関係なく読み込む
├ {環境変数名}.EXT … NODE_ENV=環境変数(developmentなど)
├ local.EXT … 環境変数に関係なく読み込む
└ local-{環境変数名}.EXT … NODE_ENV=他環境変数
構成としてはこんな感じになります。
他にも、NODE_APP_INSTANCE=xxxやHOST=xxxなどで更に細かくファイルを作ることもできますが、通常そこまで細かくする必要はないので興味がある人は公式の構成を参考にしてください。
default.EXTと{環境変数名}.EXTの両方に同じプロパティが存在する場合
もし、default.EXTと{環境変数名}.EXTに同じプロパティが存在する場合は{環境変数名}.EXTのプロパティ名が使われます。
ファイルのロード順番は下記のようになります。
なので、同じプロパティが存在する場合は下記順番の中で、最後に出現したプロパティ名が使われます。
- default.EXT
- {環境変数名}.EXT
- local.EXT
- local-{環境変数名}.EXT
- コマンドで直接指定
構成するファイルは他にもありますので、もっと詳しくロードする順番が知りたい場合はFile Load Orderを参照してください。
使い方
基本的な使い方はconfigをimport(require)してget('プロパティ名')で取得、has('プロパティ名')で存在確認をします。
# config/default.yml
api: https://api.arc-one.jp
token: abcdef
data:
format: png
---
# config/development.yml
token: ffgrtd
---
# config/production.yml
data:
filename: foobar
上記3ファイルが存在すると仮定してそれぞれgetやhasを使っていきます。
getで取得
データを取得する場合は下記のようになります。
// NODE_ENV=developmentで実行
import config from 'config'
const api = config.get('api') // -> https://api.arc-one.jp
const token = config.get('token') // -> ffgrtd
// オブジェクトを指定する場合
const format = cofig.get('data.format') // -> png
ただし、存在しないプロパティを取得しようとした場合はエラーとなります。
// NODE_ENV=developmentで実行
import config from 'config'
const filename = config.get('data.filenme') // -> ERROR!!
NODE_ENV=developmentで実行した場合data.filenameはproductionにしか存在しないためエラーとなります。
defaultやlocal以外の特定の環境変数ファイルにしか存在しないプロパティがある場合はhasを使って存在確認します。
hasを使って存在確認
import config from 'config'
let filename
if (config.has('data.filename')) {
filename = config.get('data.filename')
}
configのメリット
さて、ここまでだと別にこんなライブラリを使わなくてもNODE_ENVの変数で比較すればいいじゃんと思うかもしれません。
ここまではdevelopmentとproductionしか書いてませんがNODE_ENVは任意の値を渡すことが可能です。
そうなった時に必要な数だけの比較文をあちこちに書くことになります。
それではメンテナンスコストだけ大きくなっていきます。
しかし、configを使うことによってすでに書かれているプロパティに関しては環境変数ごとのファイルを用意するだけで済み、コード側への追記が不要になります。
もし、環境変数によってエラーが起きた場合はconfigファイルに不備があるという原因も特定しやすくなります。
要は、変動する設定ファイルとロジックは分けることによって責任範囲を明確化できるというのはメリットが大きいと思っています。
Configure your Node.js Applications: config
