【nuxt, axios】nuxt build した後から環境変数を反映させたい【RuntimeConfig】

# TL; DR

1. `@nuxtjs/axios` のバージョンが `5.12.0` 以上であることを確認 (`package.json`を参照)
2. [Options | Axios Module](https://axios.nuxtjs.org/options.html#runtime-config) の **#runtime-config** をよく見て，`nuxt.config.js` に追記する．
3. `nuxt start` 時に環境変数を渡しても読み込んでくれるようになる！

```js:nuxt.config.js
export default {
  modules: [
    '@nuxtjs/axios'
  ],

  axios: {
    baseURL: 'http://localhost:4000', // Used as fallback if no runtime config is provided
  },

  publicRuntimeConfig: {
    axios: {
      browserBaseURL: process.env.BROWSER_BASE_URL
    }
  },

  privateRuntimeConfig: {
    axios: {
      baseURL: process.env.BASE_URL
    }
  },
}
```

```terminal
$ BROWSER_BASE_URL=http://example.com BASE_URL=http://my-container nuxt start
```

※ 以下の方法でも渡せる

 - `.env` ファイル
 - `docker` の引数として渡す
 - `docker-compose` の `environment` として渡す

# 背景：`axios`に対して，ビルドした後に環境変数を渡したい

nuxtをdockerでデプロイしようとしたときに，サーバが貧弱すぎてビルド出来なかった．代わりにローカルでビルドしてからそれを *docker image* として焼いてから [Docker Hub]() にプッシュして，サーバ側ではすでにビルド済みのイメージを引っ張ってくればいいと考えた．しかし，**ビルド時に渡していた環境変数を読み込んでくれない** という問題に直面した．具体的には，`@nuxtjs/axios` で *production* と *development* 環境を切り分けたいと考えていたが，このままでは *production* 時の値をサーバ側で持てないことになる．

# 調査

[`axios` のオプション](https://axios.nuxtjs.org/options)で解決できないかな～～と調べていた矢先，前回見たときには絶対になかったであろう [Runtime Config オプション](https://axios.nuxtjs.org/options.html#runtime-config) を見つける．

> Runtime Config
**The use of runtime config is mandatory in case of using environment variables in production**, otherwise, the values will be hard coded during build and won't change.
> 
> 訳：本番環境で環境変数を使用する場合は，ランタイムコンフィグの使用が**必須**です．もし使わない場合は，その値はビルド中にハードコードされ，変更されることはありません．
> cf.  [Runtime Config - NuxtJS](https://nuxtjs.org/guide/runtime-config) (参照：2020/07/15)

まさしくやりたいことそのままでウケてしまった（フロントをやっているとこういう開発の最先端に居ることを実感して楽しいね）

で，*RuntimeConfig* とはなんぞやと思って[リンク先](https://nuxtjs.org/guide/runtime-config)を参照すると，`nuxt v2.13.x` での新機能であることも判明した．

> Nuxt.js supports env config to provide configuration via process.env. This is done by webpack's DefinePlugin.
> 
> **This approach had two downsides**:
>
> - Values are read during build time and persisted into webpack bundle. So for a change to process.env we need to rebuild which is against 12factor app design
> - It can easily mislead to expose secret keys to client-side bundle
>
> You can learn more about why we are [moving from @nuxtjs/dotenv to runtime config](https://nuxtjs.org/blog/moving-from-nuxtjs-dotenv-to-runtime-config).
> 
> cf. [Moving from @nuxtjs/dotenv to runtime config - NuxtJS](https://nuxtjs.org/blog/moving-from-nuxtjs-dotenv-to-runtime-config) （参照：2020/07/15）

だいたいの意味としては「今までのやり方だと欠点が2つほどあったから，*RuntimeConfig* に乗り換えることで改善しようぜ！」ということらしい．巷であふれる `@nuxtjs/dotenv` くんのやり方をウゼェと思って触れなかった直感が見事にあたってしまったな……

# 実践

　何も考えずドキュメントをコピペしてはビルドし，おかしいな～～と首を傾げてはビルドするという地獄をひたすら繰り返した後，こんなにも思い通りに行かないのはおかしいと思い始める．で，「実はまだ実装されていない未来の機能だったのでは？」あるいは「あまりにも古すぎる文献にあたってしまい破壊的変更があったのでは？」との結論にいたり，リリースノートを探し始める．
　そこまで手を動かし始めてから10時間が経っていたが，ようやくたどり着いた結論として「**ごく最近になって実装された機能であり，誰も実践して文書にまとめていない**」ものだったことが発覚．誰の意見も参考にならないはずだわ……

- [feat: runtime config and built-in dotenv support by pi0 · Pull Request #7312 · nuxt/nuxt.js](https://github.com/nuxt/nuxt.js/pull/7312)
- [CHANGELOG.md#5120-2020-07-10 | support runtimeConfig](https://github.com/nuxt-community/axios-module/blob/master/CHANGELOG.md#5120-2020-07-10) 

もちろん今のヴァージョンのままでは通用しないのであった．俺の10時間を返してくれ……

# 結論

1. `@nuxtjs/axios` を最新のものにアップデートして，
2. `nuxt.config.js` に *RuntimeConfig* に関する記述を追加して，
3. どうにかしてビルドした後に環境変数を渡す！
 - `.env` ファイル
 - コマンドラインで `nuxt start` する直前に定義して渡す
 - `docker` の引数として渡す
 - `docker-compose` の `environment` として渡す

# *Contributor* からの提言

> 🚫 Don’t commit sensitive values or secret keys to git
> 🚫 Don't store secret keys or sensitive values in your nuxt.config or .env unless is gitignored
> ✅ Use default values for runtimeConfig such as process.env.baseURL || 'https://nuxt.js.org'
> ✅ Store secret keys correctly using your hosting platform such as on Heroku or Netlify etc
> ✅ Follow JS naming convention (secretKey rather than SECRET_KEY) for runtimeConfig
> ✅ Prefer using runtimeConfig rather than env option

#　小ワザ

*baseURL* に関する記述は *RuntimeConfig.axios* に移動できる． 

　`Used as fallback if no runtime config is provided` となっているが，トップレベルでの `axios`以下に `baseURL`, `browserBaseURL`を指定すると，「**ビルド時点での**」値を渡すことになっているためである．もし指定しなかった場合，デフォルト値として「localhost:3000」をとる．
　しかし，*RuntimeConfig* に対して以下のように論理和を取れば，当該の環境変数（この例でいうと `BROWSER_BASE_URL`）が指定されなかったときの代理文字列（この例だと`https://nuxtjs.org`）を指定することが出来る．すなわち，ビルドの段階ではデフォルト値 `localhost:3000` だったものを **代理文字列で上書きできる** ため，以下の記法で指定するときは `axios` 直下の *baseURL* オプションが不要になる．

```js:nuxt.config.js
  // RutimeCOnfig以下に論理和として書けば，axios オプションは省略可能
  // axios: {
  //  baseURL: 'http://localhost:4000', 
  // },
  publicRuntimeConfig: {
    axios: {
      browserBaseURL: process.env.BROWSER_BASE_URL || 'https://nuxtjs.org'
    }
  },
```

# 感想

　ローカルPCでビルドしてリモートサーバでイメージを引っ張ってくるだけで良くなったのが個人的には最高！って感じ（サーバ環境のスペックをあんまり意識しなくていいので）

きちんと原文にもあたりましょうという話でした（ n 回目）

# 参考文献（参照：2020/07/15）

- [Options | Axios Module](https://axios.nuxtjs.org/options.html)
    - 今回の ~~戦犯~~ 主役
    - 「ドキュメントを読むときは，きちんとヴァージョンにも留意しようね？」
- [Runtime Config - NuxtJS](https://nuxtjs.org/guide/runtime-config)
    - `axios` 以外にも様々環境変数を混ぜ込むことが出来るらしいが，今の所触れていない．調べて記事を書いてくれる方を募集しています（丸投げ）
- [moving from @nuxtjs/dotenv to runtime config](https://nuxtjs.org/blog/moving-from-nuxtjs-dotenv-to-runtime-config)
    - *Contributor* の生の声が聞ける．ここが一番サンプルが多いので，これを見つつやり方を検討すると良いと思われる．
- [feat: runtime config and built-in dotenv support by pi0 · Pull Request #7312 · nuxt/nuxt.js](https://github.com/nuxt/nuxt.js/pull/7312)
    - こういうデカいプロジェクトの `issue` とか `PR` とか見るとほんまスゲ～～ってなるし，どういうやり方でやれば良いのか参考にしやすいので，なるたけ読めるようになっておきたい．
- [CHANGELOG.md#5120-2020-07-10 | support runtimeConfig](https://github.com/nuxt-community/axios-module/blob/master/CHANGELOG.md#5120-2020-07-10)