17
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rails の Credentials を Webpacker で管理するフロント側に共有したい

Last updated at Posted at 2020-02-04

賛否ありますが、Railsにベッタリの開発をしていると、なるべくRailsに寄せて考えたくなることがあります。

そんな

  • Webpacker 使ってるプロジェクト
  • 環境によって変わる値を環境変数ではなく Credentials にまとめたい。分散させたくない。
  • フロントエンドでも使いたい。たとえば Firebase の Project ID や API Key のようなDev,Stg,Prod環境ごとに違って、かつフロントエンドコードに含めて問題ないもの。

そんなときの話です。

  • webpack 4.41.5
  • webpack-dev-server 3.10.2
  • webpacker 4.2.2
  • Rails 6.0.1

Webpacker::Compiler.env は bin/webpack-dev-server を使うとうまくいかない

こんな Credentials があったとき

# bundle exec rails credentials:edit --environment=development
firebase:
  project_id: FIREBASE_PROJECT_ID
  api_key: FIREBASE_API_KEY
  auth_domain: FIREBASE_PROJECT_ID.firebaseapp.com

Rails から Webpacker に環境変数を渡すには Webpacker::Compiler.env が使えます。
Pass custom environment variables to the compiler #691

config/initializers/webpacker.rb
Webpacker::Compiler.env["FIREBASE_API_KEY"] = Rails.application.credentials.dig(:firebase, :api_key)
Webpacker::Compiler.env["FIREBASE_AUTH_DOMAIN"] = Rails.application.credentials.dig(:firebase, :auth_domain)
Webpacker::Compiler.env["FIREBASE_PROJECT_ID"] = Rails.application.credentials.dig(:firebase, :project_id)
app/javascript/src/firebase.js
import * as firebase from "firebase/app";
import "firebase/auth";

// "FIREBASE_PROJECT_ID: FIREBASE_PROJECT_ID"
console.log(`FIREBASE_PROJECT_ID: ${process.env.FIREBASE_PROJECT_ID}`);

const config = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID
};

firebase.initializeApp(config);

export default firebase;

これは、Rails Server にアクセスした際にビルドする場合や、 assets:precompile でビルドする場合には動くのですが、 bin/webpack-dev-server では動作しません。

困ったことに bin/webpack-dev-server を使ったHMRの快適さに慣れると、いちいちF5なんてやってられません。

$ bin/webpack-dev-server
app/javascript/src/firebase.js
import * as firebase from "firebase/app";
import "firebase/auth";

// "FIREBASE_PROJECT_ID: undefined"
console.log(`FIREBASE_PROJECT_ID: ${process.env.FIREBASE_PROJECT_ID}`);

const config = {
  apiKey: process.env.FIREBASE_API_KEY,
  authDomain: process.env.FIREBASE_AUTH_DOMAIN,
  projectId: process.env.FIREBASE_PROJECT_ID
};

firebase.initializeApp(config);

export default firebase;

Webpacker::Compiler.env はどう使われるか?

Webpacker::Compiler.compile

rails assets:precompile を実行したり、ビュー中で javascript_pack_tag ヘルパーを実行したりすると、 Webpacker::Compiler.compile が呼ばれます。( javascript_pack_tagwebpack-dev-server を実行していないときのみ。このへんのコードによる )

Webpacker::Compiler.compileWebpacker::Compiler.env の値を環境変数に設定した上で bin/webpack を実行します。

bin/webpack-dev-server

bin/webpack-dev-server を実行したときには、 Webpacker::Compiler.compile は呼ばれません。
config/webpacker.yml から設定を読み出すなどした後、直接 node_modules/.bin/webpack-dev-server を実行します。このとき環境変数として Webpacker::Compiler.env を渡します。

Webpacker と環境変数

通常、Webpack では、JavaScriptモジュール中の process.env.HOGE の値は未定義値となります。

Webpacker では標準で Webpack の EnvironmentPlugin を使って、これができるようになっています。
Webpacker しか知らないと参照できて当たり前のように感じてしまいますが、これは Webpacker が気を回してくれてたからなんですね。

config/webpack/environment.js
const { environment } = require("@rails/webpacker"); // この environment

// こうすると EnvironmentPlugin があることがわかる
console.log(environment.plugins);

module.exports = environment;

この2つの仕組みにより Webpacker::Compiler.env に設定した値を JavaScript モジュール内で参照できるようになっています。

bin/webpack-dev-server ではなぜ Webpacker::Compiler.env が機能しないか

上記

bin/webpack-dev-server を実行したときには、 Webpacker::Compiler.compile は呼ばれません。
config/webpacker.yml から設定を読み出すなどした後、直接 node_modules/.bin/webpack-dev-server を実行します。このとき環境変数として Webpacker::Compiler.env を渡します。

の通り、 bin/webpack-dev-server でも Webpacker::Compiler.env は使っています。
しかしながら、 assets:precompilejavascript_pack_tag と違い、機能しません。

これは、 bin/webpack-dev-server では、Railsを起動しないため config/initializers/webpacker.rb に書いたコードは実行されないためです。

bin/webpack-dev-server でも Webpacker::Compiler.env を使う

A. config/initializers/webpacker.rb を実行する

bin/webpack-dev-server に次の2行を追加すると、Railsの初期化が走り、 initializers で Webpacker::Compiler.env を設定できます。
しかし「Rails.application.credentials にアクセスしたい」だけなのに、これはやり過ぎです。

require_relative '../config/application'
Rails.application.initialize!

B. config/initializers/webpacker.rbbin/webpack-dev-server でコードを共有する

Rails.application.credentials はRailsの初期化処理なしでもアクセスできます。

たとえば次のようにして共有できます。

config/webpacker_env.rb
Webpacker::Compiler.env["FIREBASE_API_KEY"] = Rails.application.credentials.dig(:firebase, :api_key)
Webpacker::Compiler.env["FIREBASE_AUTH_DOMAIN"] = Rails.application.credentials.dig(:firebase, :auth_domain)
Webpacker::Compiler.env["FIREBASE_PROJECT_ID"] = Rails.application.credentials.dig(:firebase, :project_id)
config/initializers/webpacker.rb
require_relative "../webpacker_env"
# 追加
require_relative '../config/application'
require_relative "../config/webpacker_env"

あとがき

がんばって Rails / Webpacker でなんとかするために Webpacker のコード読むぐらいなら Webpacker 捨てて Webpackの流儀でやれ、という気持ちになりましたが、一応動かすことはできました。

設定情報の二重管理をせず、手間をかけず、もっと自然に共有する方法はないものだろうか?

17
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?