ここのところ、クライアントサイドJSとサーバサイドで設定の共有をしたいなと思うことがよくあった。拙いながらその方法について考えてみた。
やりたいこと
├── app
├── config
│ ├── production
│ │ ├── public.json
│ │ └── private.json
│ ├── staging
│ │ ├── public.json
│ │ └── private.json
│ ├── public.json
│ └── private.json
├── public
まず、サーバサイド(ここではapp
ディレクトリ以下)とクライアントサイド(ここではpublic
以下)で設定を共有したいこと自体はよくあると思う。
ユーザーがコンテンツを投稿できるようなアプリケーションの場合、たとえばユーザーの投稿数の制限だとか、文字の最大数の制限だとか、バリデーション周りで頻出する。
それで別々に設定を書くだとか、設定をサーバサイドからHTTPリクエストで受け取るだとかは、あまりスマートじゃないと考えていて、クライアントサイドはBrowserifyとかWebpackでJSONファイルをrequire
するような形で読み込めればいいと思った。
設定の中には、DBへ接続する情報だとか、OAuth認証のConsumer Keyだとか、クライアントサイド側には渡したくない設定もあって、これはprivate.json
のような形で別々にする必要がある。
また、設定は環境によって変えたいことが当たり前なので、環境ごとにディレクトリを作り、production/private.json
のように分けておけるといいと思った。
やり方
実装としてはこうなった。下記リポジトリを参照。
axross/config_management_sample | Github
https://github.com/axross/config_management_sample
// returns default config
// var CONFIG = require('../config')();
// returns production config
// var CONFIG = require('../config')('production');
// also
// var CONFIG = require('../config')(process.env.NODE_ENV);
// for client-side js
// var CONFIG = require('../config/bundle.json');
色々融通を利かせてるうちにindex.js
のコードが長くなってしまったけど、それなりっぽくなったかなって感覚はあって、たとえばNode.js側からはvar CONFIG = require('./config')(process.env.NODE_ENV)
って具合に呼べるようにできた。
また、いずれpublic
やprivate
だけではなく、フィールドやファイルを分けたくなるシーンも発生するような気がしたので、同じ階層にある別名のJSONファイルは、ファイル名を大文字化した上で設定の中に巻き込むようにした。
クライアントサイド向けにJSONファイルを書き出しているのは、Privateな設定を隠すのと、Browserifyとかでこのindex.js
もバンドルされるのは気持ち悪いなと思ったから。
クライアントサイドでの実際の使い方としては、gulpのビルドタスクなどでconfig.generateBundle('production', function() { ... })
のように実行してやる。
すると、bundle.json
が生成されるので、これをrequireするようにすればよい。
また、Webpackの場合、別途JSONローダーが必要になるんだけど、そのJSONローダーを使ってどうやってもJSONファイルが読み込めなかったので、Webpackを使うならmodule.exports =
という文字を先頭に足して、JSONではなくJavascriptファイル化してやる必要がある。
問題かつ本題
設定の共有こそできるものの、クライアントサイドとサーバサイドで設定にアクセスするAPIが違うのは気持ち悪い(サーバーはrequire('./config')
、クライアントはrequire('./config/bundle.json')
)。
これを解決できるうまい方法があったら教えてください。