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];
})
JP.
  • 5,507
  • 15
  • 59
  • 100
  • I've found [envify](https://www.npmjs.com/package/envify-loader) but I'm having a hard time configuring it. – JP. Feb 25 '15 at 12:10
  • 1
    There is an plugin, which allows to whitelist environment var which should be inlined: `new webpack.EnvironmentPlugin(["AUTH_SERVER_URL", "CONTENT_SERVER_URL"])` – Tobias K. Feb 28 '15 at 00:15
  • 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. – Chris Cinelli Jan 19 '17 at 22:31

4 Answers4

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'
    ])
  ]
}
owerme
  • 270
  • 3
  • 6
  • 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? – Levi Botelho Aug 13 '16 at 18:05
  • 1
    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'`. – owerme Aug 15 '16 at 18:20
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.

psiyumm
  • 6,437
  • 3
  • 29
  • 50
  • Out of interest, on what grounds is the other method deprecated? – JP. Jul 20 '15 at 22:34
  • 2
    Whatever was being done in `envify-loader` was just a wrapper. Discussion [here](https://github.com/mjohnston/envify-loader/issues/1). – psiyumm Jul 21 '15 at 06:24
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 :)

JP.
  • 5,507
  • 15
  • 59
  • 100
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.

Chris Cinelli
  • 4,679
  • 4
  • 28
  • 40