How to pass .env file variables to webpack config?
Asked Answered
M

9

76

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"
    } 
Mitra answered 14/9, 2017 at 17:20 Comment(4)
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.Nephew
@Nephew 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.Stephan
@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!Nephew
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.Mitra
S
138

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),
}),
Stephan answered 15/9, 2017 at 5:21 Comment(14)
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?Zaibatsu
I had to turn the parsed dotenv into a valid json string with JSON.stringify(dotenv.parsed) for this to work.Chou
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.Dicrotic
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)Jefferey
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.Leitmotif
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.Leitmotif
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.Vassallo
@DannyCoulombe shouldn't this line require('dotenv').config({ path: './.env' }); load only the env from that file?Boxwood
@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.Vassallo
@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.Stephan
@Computer'sGuy i hope above comment also answers your confusion.Stephan
@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).Vassallo
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.Grime
@Grime I thought that I could search by source code on Github and find projects that were implementing just that and found this one: github.com/dsuryd/dotNetify/blob/… And now, if you go on their main Github page, you'll find their website and on it, you'll see this file: 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.Vassallo
J
25

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)
    })
]

...
Jabin answered 9/7, 2019 at 23:33 Comment(6)
dotenv.config is not a functionCroze
@Croze It is. github.com/motdotla/dotenv/blob/…Jabin
This bypasses the functionality of .env using actual environment variables when deployed.Chemesh
@Chemesh 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.Jabin
Maybe 10 years ago it was, but certainly not anymore. Node.js has been a thing for quite a whileChemesh
@Chemesh 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.Jabin
A
12

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,
    }),
    // ...
]
Admeasurement answered 25/3, 2019 at 21:31 Comment(0)
G
10

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');
Gangland answered 18/4, 2019 at 12:45 Comment(4)
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.Varnado
It is working with me man, it seems like you missed something.Gangland
It's not. In case if you're trying to use these variables right inside your webpack.js fileRedress
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 aboutChemesh
I
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

Ignaz answered 14/9, 2017 at 21:33 Comment(2)
Why are you stringifying the API_URL? And, why don't stringify also the SECRET_KEY?Tennessee
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 fileOverglaze
N
5

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.

Nephew answered 26/2, 2020 at 17:18 Comment(4)
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.Mitra
I see you have updated the question. Before it really made it seem like you were trying to pass an actual secret.Nephew
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.Mitra
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.Nephew
R
2

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')
    }
Recommend answered 16/3, 2018 at 15:35 Comment(1)
The original question was about config variables stored in a .env file, not passed via the CLI so this wouldn't work AFAIK.Varnado
O
1

In my case, I have many environments to support Ex: .env.local, .env.dev, ... etc. I have solved loading environment specific env file to webpack config as follows

Step 1: Create a file for a specific environment - Ex: .env.local

API_HOST=127.0.0.1
API_KEY=mysecretkey
...

Step 2: Create a webpack config common for all environments - webpack.common.js

...
...
const webpack = require('webpack')
const dotenv = require('dotenv')
...
...

module.exports = env => {
    return {
        resolve: {...},
        devServer: {...},
        module: {...},
        plugins: [
            new webpack.DefinePlugin(
               {'process.env': JSON.stringify(dotenv.config(path: env.ENV_FILE}).parsed)})
  }
}

Here the important thing to be noted is module.exports should return a function instead of an object so that it can access the env variable (ENV_FILE in this example) passed on by the environment specific webpack config file, such as webpack.local.js (step 3).

Step 3: Create a environment specific webpack config file - webpack.local.js

const { merge } = require('webpack-merge')
const common = require('./webpack.common')

module.exports = env => merge(common(env), {
    mode: 'development',
    output: {
        publicPath: 'http://localhost:3000',
    },
    devtool: 'inline-source-map',
    devServer: {
        watchFiles: ['src/**/*'],
        hot: true,
        port: 3000
    },
})

In this environment specific file, webpack.common.js file is imported as a function (const common) that gets called with env parameter as part of the webpack merge. Here env parameter is populated by the --env option in the webpack command in the package.json (step 4)

Step 4: Setup webpack commands in package.json

"scripts": {
  "start": "webpack serve --open --env ENV_FILE=.env.local --config webpack.local.js",
  "build:prod": "webpack --env ENV_FILE=.env.prod --config webpack.prod.js"
  ...
}

Here npm command start serves up a local web server with webpack.local.js as its webpack config. When the call gets into webpack.local.js it populates ENV_FILE key into the env parameter in the function returned by module.exports.

Similarly you can setup webpack.prod.js which can be specific to your prod environment on the lines of webpack.local.js illustrated above.

Orola answered 8/11, 2023 at 17:4 Comment(0)
O
0

The dotenv-flow-webpack can do this for you.

It is a webpack plugin that allows you to securely use environment variables within your javascript web application, loading them using dotenv-flow's .env* files loading strategy.

dotenv-flow extends dotenv, adding support of NODE_ENV-specific .env* files like .env.development, .env.test, .env.stage, and .env.production, and the appropriate .env*.local overrides allowing your app to have multiple environments with selectively-adjusted environment variable setups and load them dynamically depending on the current NODE_ENV.

🌱 Inspired by CreateReactApp's storing configs in .env* files approach, the Twelve-Factor App methodology in general, and its store config in the environment section in particular.

Orland answered 27/9, 2023 at 12:16 Comment(1)
Your solution is the better one, as it allows each task to set which .env file to load. For instance, for dev and prod environments.Agglutinate

© 2022 - 2025 — McMap. All rights reserved.