loading...
Cover image for How to use .env file in JavaScript applications with webpack

How to use .env file in JavaScript applications with webpack

sanfra1407 profile image Giuseppe Sanfrancesco Updated on ・5 min read

It's been awhile I haven't written a post on the blog and, since I must #StayAtHome due to the COVID-19 Pandemic, I would like to write about an interesting topic.


Table of Contents

  1. Introduction
  2. A real example
  3. Project structure
  4. Build and serve the app
  5. Conclusion

Introduction

As you have already read from the title, I'm going to show you how it's possible to read env variables from a .env file in a JavaScript application.

I guess, at this point, that many of you are asking themselves:
"WTF?! Why should I put variables in a file?! It would be better to use them inside the code!"

Well, usually an application could have different variables based on the environment. For instance: on development, staging and production it could have different URLs, different API keys, different users and so on...

So, to do that, you just need to create a .env file in the root of your project, define your variables and read them in your JavaScript code, especially to avoid to change the source code everytime you need to have different configuration.

N.B. This must be done for each environment, meaning that .env files mustn't be committed!


A real example

Let's try to create a simple front end application reading environment variables from a .env file.

npm init

First of all, we need to create the package.json file by running:

npm init

N.B. I'm going to call my app "webpack-env", but you can choose whatever you want: it doesn't matter.

webpack, babel and dotenv

Now we need to install webpack to build our application, babel-loader to compile .js files and dotenv to read and parse the .env file.

npm install webpack webpack-cli @babel/core babel-loader dotenv --save-dev

If you have done everything correct, you should have a package.json like this one:

{
  "name": "webpack-env",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "babel-loader": "^8.1.0",
    "dotenv": "^8.2.0",
    "http-server": "^0.12.1",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11"
  }
}

You can copy it and run npm install to avoid all the previous steps.


Project structure

At this point we can start crafting our project.

If every worked correctly, you should have two files:

  • package.json (created by running npm init)
  • package-lock.json (created by running npm install)

Let's go deeper creating something else.

webpack.config.js

This is the webpack's configuration file. Just use the following configuration:

const path = require("path");
const webpack = require('webpack');
const dotenv = require('dotenv').config( {
  path: path.join(__dirname, '.env')
} );

module.exports = {
  entry: "./src/app.js",
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "app.js",
  },
  module: {
    rules: [
      {
        test: /\.js?$/,
        exclude: /(node_modules)/,
        include: path.resolve(__dirname, "src"),
        use: {
          loader: "babel-loader"
        }
      },
    ]
  },
  plugins: [
    new webpack.DefinePlugin( {
      "process.env": dotenv.parsed
    } ),
  ],
};

Let's see what we have just written.

const dotenv = require('dotenv').config( {
  path: path.join(__dirname, '.env')
} );

We use dotenv library to read the .env file from the root of the project.

plugins: [
  new webpack.DefinePlugin( {
    "process.env": dotenv.parsed
  } ),
],

By using the webpack.DefinePlugin, we parse and inject the whole .env file's content which is converted into a JavaScript object and assigned to the "process.env" variable.

From now on, we can use "process.env" object inside our application.

.env file

Now it's time to add our .env file.
Let's create it in the root of the application with the following variables:

  • APP_TITLE = "My application title"
  • APP_BASE_URL = "https://foobar.test"
  • APP_API_USER = "amazing_webpack"
  • APP_ENV = "production"
  • APP_TIMEZONE = "Europe/Rome"

src/app.js

This is the source code which will be compiled by webpack:

// `process.env` is the one defined in the webpack's DefinePlugin
const envVariables = process.env;

// Read vars from envVariables object
const {
  APP_TITLE,
  APP_BASE_URL,
  APP_API_USER,
  APP_ENV,
  APP_TIMEZONE
} = envVariables;

/**
 * @const _getRowString
 * @description Concatenate `description` and `envVar` for creating a row text.
 * @param description 
 * @param envVar 
 * 
 * @returns {string}
 */
const _getRowString = (description, envVar) => { 
  return `<p>${description}: <strong>${envVar}</strong></p>`;
}

// Append rows to `.env-vars` class
document.querySelector('.env-vars').innerHTML = `
  ${_getRowString('App title', APP_TITLE)}
  ${_getRowString('Current environment', APP_ENV)}
  ${_getRowString('API user', APP_API_USER)}
  ${_getRowString('Base URL', APP_BASE_URL)}
  ${_getRowString('Timezone', APP_TIMEZONE)}
`;

// Expose envVariables to the window object
window.envVariables = envVariables;

As defined in webpack.config.js, the final bundle will be put inside the public/ folder => public/app.js.

public/index.html

This file is meant be our app's entry point. It's just a simple HTML file:

<html>
  <head>
    <title>webpack env</title>
  </head>
  <body>
    <h1>Just some env variables read from a .env file!</h1>
    <div class="env-vars"></div>

    <script src="app.js"></script>
  </body>

</html>

If everything went good, this should be the final structure:

Project structure


Build and serve the app

Now it's time to compile and serve our application.

First of all we need to install a server to serve our app.
We're going to use http-server.

Let's install it:

npm install http-server --save-dev

Once we have installed it, let's define two npm scripts:

  • npm run build (to build the app)
  • npm run serve (to serve the app)

We can do it by adding two scripts in the package.json scripts object.

Let's replace the whole scripts object with the following one:

"scripts": {
  "build": "webpack --mode=production",
  "serve": "./node_modules/.bin/http-server"
},

N.B. Since we don't need to run unit tests, we can remove the test scripts.

Now it's possible to compile the app and serve it by running:

npm run build && npm run serve

In your console, you should see something like this:

Console output

If everything went good, we should see our application working; just open the url provided by http-server.
Working app


Conclusion

As you can easily understand, this approach allows you to use variables based on the environment without changing your harcoded variables each time.

You just need to set your env vars, build the app and... that's all!


Follow me on

If you liked the post, you might offer me a ☕️ on PayPal. 🙂


Posted on Nov 12 '19 by:

sanfra1407 profile

Giuseppe Sanfrancesco

@sanfra1407

🐐 Worst software engineer of the company.

Discussion

markdown guide
 

Is there a way to only use the .env file if there are no environment variables defined? Like as a fallback solution? Because when moving the app into a docker container, there will be no .env file, but there will be environment variables.