Passing Dynamic Environment Variables to VueJS Application at Run Time

Phuwadon Danrahan
absoroute.io
Published in
5 min readApr 30, 2020

Background

I’m developing a solution for a client which requires both web, api and some workers. The solution must be deployed on premise which will utilizing Docker Swarm as the Docker platform. In the early stage, we will try with Docker Compose first.

API & workers are written in Javascript (actually Typescript) by using NestJS framework. We don’t have any issue with environment settings for the server side application. Just rely on Environment variables and consume them directly from process.env with some helps from DotEnv to override settings in local development.

Passing environment variables to SPA such as VueJS is another story. Static variables are quite simple by just using DotEnv. But what if we need to supply the variable of Host IP address to be a part of the API’s URL. Firstly, I read several blogs about solving this issue and come up with the idea from this blog by José Moreira at Container-Solutions.com. However the solution in that blog includes the UI-Deployment script into VueJS’s code which I don’t like. I’m going to rename UI-Deployment script into another code repository called “ui-env-parser” and run it in another docker container.

The Final Layout of this Project

Active Dockers & Died Dockers in Docker Host

We will have 3 code repositories in this project

  1. web” → I’m using VueJS with minical code just the Hello World to simulate this integration.
  2. ui-env-parser” → this is the main script that handles environment variables replacement.
  3. ui-env-parser-compose” → this repository contains (1) docker compose configuration (2) Nginx configuration file.

Let’s deep dive into each repository.

(1) Web

After initialize the project (with vue cli), you should get “index.html” file under /public folder. Edit this file and add you customized variables there using META tag of HTML.

<meta property="VUE_APP_MY_VAR1" content="Value of MY_VAR1 from HTML" /><meta property="VUE_APP_MY_VAR2" content="Value of MY_VAR2 from HTML" />

The whole “index.html” shall looks like below.

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><meta property="VUE_APP_MY_VAR1" content="Value of MY_VAR1 from HTML" /><meta property="VUE_APP_MY_VAR2" content="Value of MY_VAR2 from HTML" /><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title><%= htmlWebpackPlugin.options.title %></title></head><body><noscript><strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><!-- built files will be auto injected --></body></html>

Then create a “config.loader.js” under /src folder. This script will check if you loaded the same variable via .env. If not, it will try to grab the value of preferred variable from META tag in “index.html” file above.

function getConfigValue(key) {
let value = '';
if (process.env && process.env[`${key}`] !== undefined) {
// get env var value
value = process.env[`${key}`];
} else {
// get value from meta tag
return getMetaValue(key);
}
return value;
}
/**
* Get value from HTML meta tag
*/
function getMetaValue(key) {
let value = '';
const node = document.querySelector(`meta[property=${key}]`);
if (node !== null) {
value = node.getAttribute('content');
}
return value;
}
export default { getConfigValue, getMetaValue };

Note: I have added the Typescript version of “config.loader.ts” into the same repository, enjoy it.

In the sample repository, I modified the HelloWorld.vue to import Config into the component and just display it.

<script>
import Config from '../config';
export default {
name: 'HelloWorld',
data: function() {
return {
my_var1: Config.VUE_APP_MY_VAR1,
my_var2: Config.VUE_APP_MY_VAR2,
};
},
};
</script>

Now you should be able to run your Vue project and should get the result like below image.

Clone this sample VueJS code from this repository.

(2) ui-env-parser

I’m the fan of NestJS which is another NodeJS framework but ships with TypeScript and really good code structure but also allow extend-ability. “ui-env-parser” in the referred blog above is just a JS file in “web” repository. However, I don’t want to mess my production code the environment preparation script so I decided to build this stand alone code to just

  • reads “index.html” from docker volume (presented by Docker-Compose).
  • modifies “index.html” of META tags to whatever Environment’s values that supplies by Docker-Compose. Note that, you can also supply DOCKER HOST’s IP Address into this Env too.
  • Saves “index.html” to a new volume (Managed by Docker-Compose). And this volume will be the main HTML folder for NGINX.

Sample source code of this script is here.

(3) ui-env-parser-compose

This is the configuration of Docker Compose and NGINX. You shall need to add the run time environment variables of VueJS into this file. It can also accept dynamic environment variable from “.env” file. You have option to add the dynamic variable there by using “KEY=VALUE” format. Then add this variable into docker-compose.yml by using ${KEY} format.

You can also override the variables that defines in (1) web in this configuration.

Summary: Code Modification Steps

  1. Create you own VueJS frontend project. Or find the sample codes here.
  • add META into “index.html”
  • copy “config.loader.js” into your source code
  • modify /src/config/index.js to cover all added variables in “index.html”
  • import “config” into your Vue component and use it.
  • build your docker image and upload to your preferred image registry.

2. Clone the “ui-env-parser” from this repo.

  • modify /src/module/worker/worker.service.ts to cover all variables in step 1.
  • you can test the script by just run “yarn start:dev” but you may get error because we didn’t supply the correct parameters (don’t worry, it will be handled by Docker Compose)
  • build your docker image and upload to your preferred image registry.

3. Clone the “ui-env-parser-compose” from this repo.

  • add your dynamic environment in “.env” file.
  • modify “docker-compose.yml” to cover variables from step 1.
  • you may need to provide the value of dynamic variable from “.env” file. In this case, just use ${VARNAME} format.
  • run “docker-compose up”

Behind the scene

  • After started docker-compose, it will try to download docker images of “web” & “ui-env-parser” from supplied url of image registries.
  • After started “web” instance, it will mount the volume “web-source” to /app/dist of “web” instance which is the compiled folder of VueJS application which includes “index.html”.
  • Then we will use “web-source” to refer as the original source of “index.html” to pass to “ui-env-parser” which will try to replace all variables and save to volume “nginx-source”.
  • Nginx will start normally with the modified “index.html” and other VueJS’s files.
  • After this step, you shall enjoy your outcome by accessing to your host’s ip address in the browser.
  • Add any other services into your docker-compose if needed.

--

--