ゆっくりのんびり。

いの (@inox_ee) です

【Node.js】Webpack でバンドルしたファイル群を Heroku にデプロイしたときに環境変数を適切に使いたい

タイトルながい

あらすじ

Github Pages をはじめ、シンプルな HTML と JS で構成された web ページなら無料で公開できるサービスは五万とあります。

ただ今回自分の作ったものには API key が含まれていたため、Heroku にデプロイすることにしました。
Heroku なら設定も楽だし大丈夫っしょ~~なんて思ってたら 1 日溶けました。つらい。
つらいのでブログにまとめました。誰かの 1 日を守れたら幸いです。

構成

<project>
|_ app/
    |_ index.html
    |_ src/
        |_ index.html
        |_ index.js
    |_ dist/
        |_ bundle.js
|_ .env(git管理外)
|_ webpack.config.js
|_ package.json
|_ app.js

やりたいこと

node ./app.js でexpressを用いたサーバを立て、src/ 以下の index.htmlindex.js を読み込む静的ページを構成。
この js ファイル内で API key を使用するため、.env環境変数を入れておきます。
また index.js は他モジュールを読み込んでいるので babel で es5 へ変換しつつ webpack でバンドルさせます。

1. dotenv

ググったらいっぱい出てくるので説明は割愛。

環境変数.env ファイルで管理するときに用いられるモジュールとして有名なものですが、webpack でバンドルしてしまうとバンドル後の js ファイルに直接書き込まれてしまうっぽい。secure じゃないので却下。

2. dotenv-webpack

上記の問題を解決した(?)モジュールが dotenv-webpack
これは dotenvWebpack.DefinePlugin をラップしたもので、環境変数の呼び出し方はほとんど dotenv と相違ありません。

github.com

dotenv は js ファイル内で require('dotenv').config() と呼び出していましたが、dotenv-webpackwebpack.config.js 内に諸設定を書き込みます(以下参照)。

/* webpack.config.js*/
const Dotenv = require('dotenv-webpack');
module.exports = {
  // 中略
  plugins: [new Dotenv()],
  // 中略
};

これで OK。 あとは呼び出したいところで process.env.SAMPLE_API_KEY と書けば API key が使用できます。

2. 余談

ちなみに .env: でなく = なので注意。

# .env
SAMPLE_API_KEY="hogehoge"

3.Heroku へのデプロイ

Heroku で環境変数の管理をするなら heroku-config をインストールしましょう。楽です。説明は割愛。

ローカルでも動くしあとはdeployだけすればおっけー!なーんて思ってたら

f:id:puyobyee18:20190714120431p:plain

うーん

結論

ローカルなら動く、さらに言えば手元でバンドルしてからデプロイしたらAPIが正しく設定されるようのでHeroku側でwebpackを動かしたときに config vars が読み込めていないっぽい。

一行一行設定を変えつつデプロイして、を繰り返した結果、 webpack.config.js に以下の設定を加えることで解決した。

plugins: [
  new Dotenv({
    systemvars: true,
  }),
],

公式のREADME曰く、

load all the predefined 'process.env' variables which will trump anything local per dotenv specs.

ということらしいのだが、なぜこの設定で行けるようになるのかは謎。

とりあえずデプロイできてよかった。

私の webpack.config.js

諸事情によりレポジトリは公開できませんが、package.jsonwebpack.config.js だけ公開しておきます(一部改変)。参考にしてください。

/* package.json */
{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node ./app.js",
    "deploy": "git push heroku staging:master",
    "build": "npx webpack",
    "heroku-postbuild": "webpack -p"
  },
  "devDependencies": {
    "dotenv-webpack": "^1.7.0",
    "eslint": "^5.16.0",
    "eslint-config-prettier": "^4.3.0",
    "eslint-plugin-prettier": "^3.1.0",
    "prettier": "^1.17.1",
  },
  "dependencies": {
    "webpack": "^4.35.3",
    "webpack-cli": "^3.3.5",
    "@babel/core": "^7.5.4",
    "@babel/preset-env": "^7.5.4",
    "babel-loader": "^8.0.6",
    "@babel/polyfill": "^7.4.4",
    "express": "^4.17.1",
  }
}
/* webpack.config.js */
const path = require('path');
const Dotenv = require('dotenv-webpack');

module.exports = {
  // メインとなるJavaScriptファイル(エントリーポイント)
  // aync/await を使うには `@babel/polyfill` を以下のように設定する
  entry: ['@babel/polyfill', './app/src/index.js'],

  // ファイルの出力設定
  output: {
    //  出力ファイルのディレクトリ名
    path: path.resolve(__dirname, 'app/dist'),
    // 出力ファイル名
    filename: 'bundle.js',
  },

  node: {
    fs: 'empty',
  }, // これもよく分からないけどとりあえずemptyにしておけば動いた。
  mode: 'development',
  plugins: [
    new Dotenv({
      path: path.resolve(__dirname, './.env'), // いらないかも。
      systemvars: true,
    }),
  ],
  module: {
    rules: [
      {
        // 拡張子 .js の場合
        test: /\.js$/,
        use: [
          {
            // Babel を利用する
            loader: 'babel-loader',
            // Babel のオプションを指定する
            options: {
              presets: [
                // プリセットを指定することで、ES2019 を ES5 に変換
                '@babel/preset-env',
              ],
            },
          },
        ],
      },
    ],
  },
};

あとがき

最近、Vsinger の 花譜 ちゃんにめちゃくちゃハマっています。
15 歳と思えぬ力強い歌声、カンザキイオリさんの楽曲の魅力を持て余すことなく歌い切る表現力 ――――― 。

今後も、もっともっと活躍してほしいです。
8/1 の初の単独ライブ『不可解』、院試のせいで行けないので泣いてる。2nd LIVE に期待。

youtu.be

ラプラスかわいい