62

I am new to webpack and worked out almost all build sections, but now the problem is that I want to pass the environment variables from a .env file to webpack config, so that I can pass that variables to my build files via webpack.DefinePlugin plugin.

Currently I am able to to pass environment variable directly from webpack to to my build. Please see the code below which I used in webpack.

new webpack.DefinePlugin({
            "API_URL": JSON.stringify("http://my-api.com"),
            "FRONT_END_API_KEY" : "MYFRONTENDKEYGOESHERE"
        }),

My package.json build script is

"scripts": {
    "start": "NODE_ENV=development webpack-dev-server --progress --port 8000 --content-base app/build/src"
    } 
ninja dev
  • 1,687
  • 2
  • 12
  • 12
  • 1
    really?! Is no one going to address the obvious issue here? If you pass a secret into DefinePlugins, it is no longer a secret!!! Only pass env variables that you are ok with being public. – NSjonas Jan 28 '20 at 03:07
  • @NSjonas No Secure Config values should be defined in the front-end ENV. The config values used in frontend can be intercepted by anyone. no matter where you save it in client-side. If you save it on client-side then it's open for anyone with basic programming skill can find it. moreover, no secure secrets should be saved at front-end .env, that has to be handled in server-side ENV. Nowadays all modern app solutions give out front-end and backend secrets separately with domain verification and all. eg: pusher,send-bird etc. Correct me if I am wrong. – Rameez Rami Feb 25 '20 at 11:27
  • @RameezRami Yes... My point is that none of these solutions point out that the author (and others below) is trying to pass secrets into an angular application! – NSjonas Feb 26 '20 at 17:04
  • 2
    I have updated the question. the whole purpose of the question was to get some value from a .env file. no one will be saving Secret API at frontend or in frontend .env . all of my secret keys are at backend .env. – ninja dev Jun 18 '20 at 15:04

7 Answers7

123

You can use dotenv package for this purpose.

npm install dotenv --save

After installing the package, add this in the top of your config:

const webpack = require('webpack'); // only add this if you don't have yet

// replace accordingly './.env' with the path of your .env file 
require('dotenv').config({ path: './.env' }); 

then in plugins section, add this:

new webpack.DefinePlugin({
  "process.env": JSON.stringify(process.env),
}),
Emeka Orji
  • 174
  • 4
  • 11
Rameez Rami
  • 5,322
  • 2
  • 29
  • 36
  • I tried this solution but for string variables it returns an object and not a string. for example, if i have DB_USERNAME=MY_USRNAME, and i try console.log(process.env.DB_USERNAME), it gives me MY_USRNAME object, a not "MY_USRNAME". i'm using dotenv 5.0.0. How can I solve it? – retrobitguy Feb 12 '18 at 11:26
  • 27
    I had to turn the parsed dotenv into a valid json string with `JSON.stringify(dotenv.parsed)` for this to work. – Sanderfish Dec 30 '18 at 14:25
  • 1
    You can also just make it more specific and do `"process.env.YOUR_VARIABLE": dotenv.parsed.YOUR_VARIABLE"` to avoid dealing with the whole object. I only needed one variable in the webpack.config.js. – nicholascm Mar 12 '20 at 18:11
  • 3
    The line `"process.env": JSON.stringify(dotenv.parsed)` only passed variables defined in .env file, losing all the env vars set in the shell. I had to require dotenv and then pass the actual process.env e.g.: `"process.env": JSON.stringify(process.env)` – Young Suk Ahn Jul 15 '20 at 19:59
  • The line `"process.env": JSON.stringify(process.env);` shouldn't have a semicolon at the end, as it is an object key/value. It's a small thing though. – JCollier Nov 09 '21 at 04:43
  • The big issue I had specifically with this answer was that my `bundle.js:formatted` had errors when I ran that line with a stringified object. I ended up having to be hacky and just hand it a string `const` like so: `"process.env": myEnvironmentString`, and although it was awkward (it passed a variable instead of a string to my React app), I was able to make it work. Your answer pointed me in the right direction. – JCollier Nov 09 '21 at 04:44
  • I'm not 100% sure, but I think the last snippet ("process.env": JSON.stringify(process.env),) is an horrible idea. You are exposing all secrets in your compilation. If this goes into a pipeline, ALL of your system environment variables are going to be exposed. On AWS, it means all of your secrets. – Chololoco Mar 07 '23 at 12:49
  • @DannyCoulombe shouldn't this line require('dotenv').config({ path: './.env' }); load only the env from that file? – Computer's Guy Mar 27 '23 at 08:48
  • @Computer'sGuy I think dotenv adds up on top of what's already in process.env. Again, not 100% sure, but I saw one of our junior push a similar thing in production once and we had all of our secrets exposed. All passwords, all secret keys, everything. – Chololoco Mar 27 '23 at 21:25
  • @Chololoco You have to clearly separate frontend ".env" and backend ".env". Your secret keys shouldn't be on frontend ".env". in frontend env parsing occurs only at the build time. not at run time like a express server or similar. I hope this clears the confusion. – Rameez Rami Apr 26 '23 at 07:01
  • @Computer'sGuy i hope above comment also answers your confusion. – Rameez Rami Apr 26 '23 at 07:05
  • 1
    @RameezRami you don't understand. Your secrets aren't in .env. This file only adds up to the existing environment variables you already have in your OS. That's probably fine if you build locally, but if you have a job for instance that triggers a build on you CI whenever you push on a branch, that environment definitely has secrets and they will be exposed if you do JSON.stringify(process.env). – Chololoco Apr 27 '23 at 19:14
  • 1
    Agree with @Chololoco, this is really dangerous. It bundles all the env variables on the OS (not just the .env files), which may contain all sorts of secrets. It'll work, but I'd keep it well away from any non-local environment. – garryp Jul 04 '23 at 20:53
  • 1
    @garryp I thought that I could search by source code on Github and find projects that were implementing just that and found this one: https://github.com/dsuryd/dotNetify/blob/4d98aac878d38a941525854f0238090f3af27b3f/DevApp/webpack.config.js#L55 And now, if you go on their main Github page, you'll find their website and on it, you'll see this file: https://dotnetify.net/Scripts/Core/app.js Now search for "process.env" in it and you'll see why this answer is an absolute catastrophy. I have chosen a project that didn't had important secrets to not expose its flaws. – Chololoco Jul 05 '23 at 23:33
23

webpack + dotenv

I did get inspiration from the accepted answer, but it doesn't work for me. Maybe the API of dotenv has changed.

The following works for me

import dotenv from 'dotenv'
import { DefinePlugin } from 'webpack'


...

plugins: [
    new DefinePlugin({
      'process.env': JSON.stringify(dotenv.config().parsed)
    })
]

...
Tyler Liu
  • 19,552
  • 11
  • 100
  • 84
  • dotenv.config is not a function – TSR May 24 '20 at 22:52
  • 1
    @TSR It is. https://github.com/motdotla/dotenv/blob/7301ac9be0b2c766f865bbe24280bf82586d25aa/lib/main.js#L77 – Tyler Liu May 25 '20 at 22:09
  • This bypasses the functionality of .env using actual environment variables when deployed. – Liam Mar 16 '23 at 09:09
  • @Liam webpack is mainly for bundling JS code to run in browser. And in browser you cannot access actual environment variables anyway. So I think the env vars is mainly for bundling time. – Tyler Liu Mar 17 '23 at 16:12
  • Maybe 10 years ago it was, but certainly not anymore. Node.js has been a thing for quite a while – Liam Mar 17 '23 at 17:45
  • @Liam For it not to override actual env vars, just rename 'process.env' to something else like "bundled.env". Then "bundled.env" for bundled env and "process.env" for actual env. – Tyler Liu Mar 17 '23 at 20:45
11

It doesn't match your case exactly (although partially), but I've found this formula to be working best for me.

I use a combination of 2 libs: dotenv to read the .env file for the webpack.config.js (configuration) needs, and webpack-dotenv-plugin for the validation (based on .env.example file) and to pass all the vars from .env file to the application code:

Part of my webpack.config.js:

// this is to load env vars for this config
require('dotenv').config({ // it puts the content to the "process.env" var. System vars are taking precedence
    path: '.env.webpack',
});
// and this to pass env vars to the JS application
const DotenvPlugin = require('webpack-dotenv-plugin');

plugins section:

plugins: [
    // ...
    new DotenvPlugin({ // makes vars available to the application js code
        path: '.env.webpack',
        sample: '.env.webpack.example',
        allowEmptyValues: true,
    }),
    // ...
]
Roman86
  • 1,990
  • 23
  • 22
8

The simplest solution I found is to use this npm package: dotenv-webpack

Create a .env file

// .env
DB_HOST=127.0.0.1
DB_PASS=foobar
S3_API=mysecretkey

Add it to your Webpack config file

// webpack.config.js
const Dotenv = require('dotenv-webpack');

module.exports = {
...
plugins: [
new Dotenv()
]
...
};

Use in your code

// file1.js
console.log(process.env.DB_HOST);
// '127.0.0.1'
Resulting bundle
// bundle.js
console.log('127.0.0.1');
Mahmoud Abd AL Kareem
  • 615
  • 2
  • 10
  • 23
  • 5
    I have this exact setting and unfortunately the `dotenv-webpack` plugin does not pass those variables to the `webpack.config.js` file, only the production code. – Blaiz Dec 16 '19 at 22:58
  • It is working with me man, it seems like you missed something. – Mahmoud Abd AL Kareem Jan 05 '20 at 13:09
  • 3
    It's not. In case if you're trying to use these variables right inside your webpack.js file – the one Mar 05 '20 at 07:45
  • This is a different problem. This plugin doesn't allow you to use dotenv in the webpack.config, which is what the question is asking about – Liam Mar 16 '23 at 09:08
5

I can't comment to clarify any info so my apologies for the answer.

You could do:

var env = require('.env');

then

new webpack.DefinePlugin({
            "API_URL": JSON.stringify("http://my-api.com"),
            "SECRET_KEY" : "MYSECRETKEYGOESHERE",
            "env_property": env.property
        }),

But I'm making assumptions about your .env file and the way its set up with this answer

DevinFlick
  • 121
  • 4
  • 1
    Why are you stringifying the `API_URL`? And, why don't stringify also the `SECRET_KEY`? – Matteo Gaggiano Feb 21 '18 at 14:59
  • Could you elaborate what assumptions you are making? this solution looks interesting in that you are exposing the value of the variable in your webpack.config file – jwknz Aug 28 '18 at 21:33
4

First off...

It appears that you are trying to pass secrets into an angular application.

There is no such thing as a "secret" in client side (browser) javascript!!!

Anything passed into DefinePlugin can be extracted with minimal effort.

Now that we've cleared that up....

Webpack now has the Environment Plugin which makes it a bit easier to pass env variables into the GlobalDefine plugin. From the docs:

new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG']);

This is equivalent to the following DefinePlugin application:

new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
  'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
});

If you are using dotenv to manage environment vars, you can use the dotenv webpack plugin.

It will only include variables that are referenced in your code, so as long as you don't reference your secrets, they won't be included.

NSjonas
  • 10,693
  • 9
  • 66
  • 92
  • Exactly. I have no doubt about that. anything given on front can be extracted. the whole question was for the idea of getting the variables from a .env file. ill be saving MAP_FRONT_END_KEY, ALGOLIA_SEARCH_KEY, similar front end API keys. – ninja dev Jun 18 '20 at 15:01
  • I see you have updated the question. Before it really made it seem like you were trying to pass an actual secret. – NSjonas Jun 18 '20 at 23:49
  • Bro, it's just a sample text. instead of typing BLA_BLA, I typed YOUR_SECRETKEY_GOES _HERE. the whole point of the question is to get value from .env. – ninja dev Jun 19 '20 at 11:22
  • Bro Brah, variable names are significant, especially in "example" code with limited context. If you put `"SECRET_KEY" : "MYSECRETKEYGOESHERE"`, of course I'm going to assume you believed this value would be kept secret. There's a serious issue with people included secrets in their SPA, so I'm going to leave the first half of my answer as is. Ty for updating the question so others won't assume it's ok to do this. – NSjonas Jun 21 '20 at 22:18
1

From webpack docs:

The webpack command line environment option --env allows you to pass in as many environment variables as you like. Environment variables will be made accessible in your webpack.config.js. For example, --env.production or --env.NODE_ENV=local (NODE_ENV is conventionally used to define the environment type, see here.)

in your package.json

webpack --env.NODE_ENV=local --env.production --progress

in your webpack.config.js

module.exports = env => {
  // Use env.<YOUR VARIABLE> here:
  console.log('NODE_ENV: ', env.NODE_ENV) // 'local'
  console.log('Production: ', env.production) // true

  return {
    entry: './src/index.js',
    output: {
      filename: 'bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
Jalal
  • 3,308
  • 4
  • 35
  • 43
  • The original question was about config variables stored in a `.env` file, not passed via the CLI so this wouldn't work AFAIK. – Blaiz Dec 16 '19 at 23:00