Environment Variables in an isomorphic JS app: Webpack find & replace?
Asked Answered
S

4

14

I'm using webpack to bundle an isomorphic JS app (based on this example) so that the browser runs the same code as the server. Everything is running smoothly except I have a config.js with some settings which are pulled in from environment variables on the server:

module.exports = {
  servers:
    auth: process.env.AUTH_SERVER_URL,
    content: process.env.CONTENT_SERVER_URL
  }
}

On the server this is grand, but when webpack renders this for the client process is empty and this doesn't work.

I'm hoping there's a kind of 'find and replace' webpack plugin that will replace them with their content in that file alone?

"…config.js content…".replace(/process\.env\.([a-z0-9_]+)/, function(match, varName) {
  return process.env[varName];
})
Sleepless answered 25/2, 2015 at 11:26 Comment(3)
I've found envify but I'm having a hard time configuring it.Sleepless
There is an plugin, which allows to whitelist environment var which should be inlined: new webpack.EnvironmentPlugin(["AUTH_SERVER_URL", "CONTENT_SERVER_URL"])Oleoresin
The problem is that the machine that build the environment is not always the one that one that run the code (this is true in our case). One way is to put them in global and in window in the HTML but it is not my favorite solution.Gaze
W
12

In your webpack.config.js, use the following preLoaders (or postLoaders),

  module: {
    preLoaders: [
      { test: /\.js$/, loader: "transform?envify" },
    ]
  }

Another way using the webpack.DefinePlugin:

plugins: [
    new DefinePlugin({
      'process.env': Object.keys(process.env).reduce(function(o, k) {
        o[k] = JSON.stringify(process.env[k]);
        return o;
      }, {})
    })
]

NOTE: The old method using envify-loader was deprecated:

DEPRECATED: use transform-loader + envify instead.

Wrier answered 20/7, 2015 at 13:22 Comment(2)
Out of interest, on what grounds is the other method deprecated?Sleepless
Whatever was being done in envify-loader was just a wrapper. Discussion here.Wrier
S
16

Note that using the DefinePlugin as suggested in the accepted answer is potentially a dangerous action as it completely exposes process.env. As Tobias commented above there's actually a plugin EnvironmentPlugin that does exactly this with an added whitelisting ability, using DefinePlugin internally.

In your webpack.config.js:

{
  plugins: [
    new webpack.EnvironmentPlugin([
      'NODE_ENV',
      'WHITELISTED_ENVIRONMENT_VARIABLE'
    ])
  ]
}
Smashandgrab answered 18/12, 2015 at 13:54 Comment(2)
I don't understand how webpack knows to replace NODE_ENV with "production", for example. With envify you need to tell it with what to replace NODE_ENV -- how does this work?Alishiaalisia
Webpack doesn't really replace any values here, it will inject whatever you whitelist from process.env into your code with whatever values already set. You need to set your environment variables yourself before the compilation starts. From the command line NODE_ENV=production webpack, or maybe in your Webpack config file process.env.NODE_ENV = 'production'.Smashandgrab
W
12

In your webpack.config.js, use the following preLoaders (or postLoaders),

  module: {
    preLoaders: [
      { test: /\.js$/, loader: "transform?envify" },
    ]
  }

Another way using the webpack.DefinePlugin:

plugins: [
    new DefinePlugin({
      'process.env': Object.keys(process.env).reduce(function(o, k) {
        o[k] = JSON.stringify(process.env[k]);
        return o;
      }, {})
    })
]

NOTE: The old method using envify-loader was deprecated:

DEPRECATED: use transform-loader + envify instead.

Wrier answered 20/7, 2015 at 13:22 Comment(2)
Out of interest, on what grounds is the other method deprecated?Sleepless
Whatever was being done in envify-loader was just a wrapper. Discussion here.Wrier
S
3

Yeah; looks like envify-loader was the easy solution.

I just added the following to my webpack loaders:

{
  test: /config\.js$/, loader: "envify-loader"
}

And the config.js (and only that file) is modified to include any referenced environment variables statically :)

Sleepless answered 25/2, 2015 at 12:12 Comment(1)
This method is deprecated!Wrier
G
2

I needed a way to use the env variables set on the machine that is running the code, no the env variables of the machine building the app.

I do not see a solution for this yet. This is what I did.

In publicEnv.js:

// List of the env variables you want to use on the client. Careful on what you put here!
const publicEnv = [
  'API_URL',
  'FACEBOOK_APP_ID',
  'GA_ID'
];

const isBrowser = typeof window !== 'undefined';
const base = (isBrowser ? window.__ENV__ : process.env) || {};

const env = {};
for (const v of publicEnv) {
  env[v] = base[v];
}
export default env;

In the HTML template file of the page I have:

import publicEnv from 'publicEnv.js';

...

<script>
  window.__ENV__ = ${stringify(publicEnv)};

  // Other things you need here...
  window.__INITIAL_STATE__ = ${stringify(initialState)};
</script>

So now I can get the value of the env variable on both frontend and backend with:

import publicEnv from 'publicEnv.js';

...

console.log("Google Analytic code is", publicEnv.GA_ID);

I hope it can help.

Gaze answered 20/1, 2017 at 0:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.