How to set process.env before a browserified script is run?

11,948

Solution 1

Instead of hardcoding enviroment variables here and there, use the envify plugin.

npm install envify 

This plugin automatically modifies the process.env.VARIABLE_HERE with what you passed as an argument to envify.

For example:

browserify index.js -t [ envify --DEBUG app:* --NODE_ENV production --FOO bar ] > bundle.js

In your application process.env.DEBUG will be replaced by app:*, process.env.NODE_ENV will be replaced by production and so on. This is a clean && elegant way in my opinion to deal with this.

Solution 2

You can change your entry point file, which would basically to do such setup and then require the original main file.

process.env.NODE_ENV = 'production';
require('app.js');

Other way (imo much cleaner) is to use transform like envify which replaces your NODE_ENV in the code with the string value directly.

Option 1

I think your approach should generally work, but I would't write directly to process.env since I am pretty much sure that it gets overwritten in the bundle. Instead you can make global variable like __env and then in the actual bundle code set it to process.env in your entry file. This is untested solution, but I believe it should work.

Option 2

Use localStorage and let your main script read variables from there upon initialization. You can set variables to localStorage manually or you can even let the server provide them if you have them in there. Developer would just open console and type something like loadEnv('production'), it would do XHR and store the result in the localStorage. Even with manual approach there is still an advantage that these doesn't need to hard-coded in html.

If manual doesn't sound good enough and server is a dead end too, you could just include all variables from all environments (if you have them somewhere) in the bundle and then use switch statement to choose correct ones based on some conditions (eg. localhost, production host).


Thinking about this, you are definitely out of scope of Browserify with your needs. It can make bundle for you, but if you don't want these information in the bundle, you are on your own.

Solution 3

So I've decided it's the web server's job to insert the environment variables. My scenario required different loggers per environment (e.g. 'local','test','prod').

code:

var express = require('express')
  , replace = require('replace');

...

var app = express();
var fileToReplace = <your browserified js here>;
replace({
  regex: 'ENV_ENVIRONMENT'
  , replacement: process.env.ENVIRONMENT
  , paths: [fileToReplace]
});

...

app.listen(process.env.PORT);

I hardcoded 'ENV_ENVIRONMENT', but you could create an object in your package.json and make it configurable.

This certainly works, and it makes sense because it's possibly the only server entry point you have.

Solution 4

I had success writing to a json file first, then importing that json file into anywhere that needed to read the environment.

So in my gulp file:

import settings from './settings';
import fs from 'fs';
...
fs.writeFileSync('./settings.json', JSON.stringify(settings));

In the settings.js file:

if(process.env.NODE_ENV) {
    console.log('Starting ' + process.env.NODE_ENV + ' environment...');
} else {
    console.log('No environment variable set.');
    process.exit();
}

export default (() => {
    let settings;

    switch(process.env.NODE_ENV) {
        case 'development':
            settings = {
                baseUrl: '...'
            };
            break;
        case 'production':
            settings = {
                baseUrl: 'some other url...'
            };
            break;
    }

    return settings;
})();

Then you can import the settings.json file in any other file and it will be static, but contain your current environment:

import settings from './settings.json';
...
console.log(settings.baseUrl);

I came here looking for a cleaner solution...good luck!

Solution 5

I run into this problemn building isomorphic react apps. I use the following (ok, it's a little hacky) solution:

I assign the env to the window object, ofcourse I don't expose all env vars, only the ones that may be public (no secret keys of passwords and such).

// code...
const expose = ["ROOT_PATH", "ENDPOINT"];
const exposeEnv = expose.reduce(
    (exposeEnv, key) => Object.assign(exposeEnv, { [key]: env[key] }), {}
);
// code...
res.send(`<DOCTYPE html>
// html...
<script>window.env = ${JSON.stringify(exposeEnv)}</script>
// html...
`);
// code...

then, in my applications clients entry point (oh yeah you have to have a single entry point) I do this:

process.env = window.env;

YMMV AKA WFM!

Share:
11,948
Yuriy Nemtsov
Author by

Yuriy Nemtsov

Updated on June 08, 2022

Comments

  • Yuriy Nemtsov
    Yuriy Nemtsov about 2 years

    The initial html comes from the back-end. The server has a defined process.env.NODE_ENV (as well as other environment variables). The browserified code is built once and runs on multiple environments (staging, production, etc.), so it isn't possible to inline the environment variables into the browserified script (via envify for example). I'd like to be able to write out the environment variables in the rendered html and for browserified code to use those variables. Is that possible?

    Here's how I imagine that being done:

    <html>
      <head>
        <script>window.process = {env: {NODE_ENV: 'production'}};</script>
        <script src="/build/browserified_app.js"></script>
      </head>
    </html>
    
  • Yuriy Nemtsov
    Yuriy Nemtsov over 9 years
    Are you suggesting to create a file per environment? If so, that's not possible in my setup because there is no predetermined set of environments. Developers are able to create a new environment per feature, and set their own environment variables that I need reflected on the front-end (in React). Using envify isn't possible either, as I mentioned in the question, because we build the js once, store it on the CDN and then use it many times in different environments.
  • FredyC
    FredyC over 9 years
    Ok, I think it's kinda bad having single bundle containing code for all environments, but if that suits you... If you really need it dynamic, I have updated answer.
  • Yuriy Nemtsov
    Yuriy Nemtsov over 9 years
    That's funny. It's exactly what I'm curently doing. It is a hack though, so finding a clean solution was my main motivation for asking the question. It looks like there's no built-in way to set globals ahead of time without this hack.
  • FredyC
    FredyC over 9 years
    Well you should maybe first ask how it is supposed to work. You want single bundle, so these variables cannot be included in there. Browser doesn't know anything like env variables so only way is to supply those from the outside. I've added Option 2 above if that looks "cleaner" to you.
  • FredyC
    FredyC over 9 years
    Whoever voted down on this, I would like to hear why do you find it so useless.
  • aaaaaa
    aaaaaa about 9 years
    I'd love an explanation for the downvote - or more importantly, a better solution to the problem.
  • alxgb
    alxgb almost 9 years
    "so it isn't possible to inline the environment variables into the browserified script (via envify for example)" - asker