TECH BOX

Technology blog from Web Engineer

この記事は最終更新日から4年以上経過しているため正確ではないかもしれません

node-configを使ってビルドの環境変数ごとにデータを書き換える

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.EXTNODE_ENV=productionであればproduction.EXTといった具合です。

利用可能なファイル拡張子としては下記があります。

  • .json … JSON
  • .json5 … コメント付きJSON
  • .hjson … Human JSON
  • .yaml or .yml … YAML
  • .js … JavaScript
  • .coffee … CoffeeScript
  • .cson … CoffeeScriptのオブジェクト形式
  • .properties … ドット記法形式
  • .tomlTOML ミニマル記法
  • .ts … TypeScript
  • .xml … XML。利用する場合はx2jsライブラリが必要

configで利用可能な拡張子

また、default.EXTは存在すれば必ず読み込むので環境変数に関係ない共通情報を作成することができます。

環境変数ごとに情報をまとめられるというのは大きなメリットだと考えます。

node-configの構成

node-configはプロジェクト直下にconfig/というディレクトリを作成してファイルを置く必要があります。

project/
  └ config/
      ├ default.EXT … 環境変数に関係なく読み込む
      ├ {環境変数名}.EXT … NODE_ENV=環境変数(developmentなど)
      ├ local.EXT … 環境変数に関係なく読み込む
      └ local-{環境変数名}.EXT … NODE_ENV=他環境変数

構成としてはこんな感じになります。
他にも、NODE_APP_INSTANCE=xxxHOST=xxxなどで更に細かくファイルを作ることもできますが、通常そこまで細かくする必要はないので興味がある人は公式の構成を参考にしてください。

default.EXTと{環境変数名}.EXTの両方に同じプロパティが存在する場合

もし、default.EXTと{環境変数名}.EXTに同じプロパティが存在する場合は{環境変数名}.EXTのプロパティ名が使われます。

ファイルのロード順番は下記のようになります。
なので、同じプロパティが存在する場合は下記順番の中で、最後に出現したプロパティ名が使われます。

  1. default.EXT
  2. {環境変数名}.EXT
  3. local.EXT
  4. local-{環境変数名}.EXT
  5. コマンドで直接指定

構成するファイルは他にもありますので、もっと詳しくロードする順番が知りたい場合は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ファイルが存在すると仮定してそれぞれgethasを使っていきます。

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.filenameproductionにしか存在しないためエラーとなります。
defaultやlocal以外の特定の環境変数ファイルにしか存在しないプロパティがある場合はhasを使って存在確認します。

hasを使って存在確認

import config from 'config'

let filename
if (config.has('data.filename')) {
  filename = config.get('data.filename')
}

configのメリット

さて、ここまでだと別にこんなライブラリを使わなくてもNODE_ENVの変数で比較すればいいじゃんと思うかもしれません。
ここまではdevelopmentproductionしか書いてませんがNODE_ENVは任意の値を渡すことが可能です。
そうなった時に必要な数だけの比較文をあちこちに書くことになります。
それではメンテナンスコストだけ大きくなっていきます。

しかし、configを使うことによってすでに書かれているプロパティに関しては環境変数ごとのファイルを用意するだけで済み、コード側への追記が不要になります。
もし、環境変数によってエラーが起きた場合はconfigファイルに不備があるという原因も特定しやすくなります。

要は、変動する設定ファイルとロジックは分けることによって責任範囲を明確化できるというのはメリットが大きいと思っています。


Configure your Node.js Applications: config