How to externalize and consume environment variables from a Vue App:
The project is built once and deployed to test and live environments. So, I want to externalize some variables which change through the environments (like URLs to call, domains, usernames etc.). The classical usage with .env
file variations with VUE_APP_
prefixed does not help this issue as their values are injected in the code during the build stage: They are not variables once it is built.
Trying it out, I have found a blog post making use of dotenv
and some extra configuration; but I could not put it together with the configuration in this VueCLI 3 official guide. The solution does not need to adopt a similar approach though, I am just trying to make a way out.
Probably not a useful information, but I am planning to define those environment variables in Config Maps in Kubernetes configuration.
I think I've accomplished to overcome this case. I am leaving the resolution here.
Define your environment-specific environment variables in .env.development
(for development purposes) and add them also to the Pod configuration with correxponding values.
Add a configuration.js
file somewhere in your Vue project source folder. It would act as a wrapper for determining whether the runtime is development (local) or production (container). It is like the one shown here, but importing/configuring dotenv
is not required:
export default class Configuration {
static get EnvConfig () {
return {
envKey1: '$ENV_KEY_1',
envKey2: '$ENV_KEY_2'
}
}
static value (key) {
// If the key does not exist in the EnvConfig object of the class, return null
if (!this.EnvConfig.hasOwnProperty(key)) {
console.error(`Configuration: There is no key named "${key}". Please add it in Configuration class.`)
return
}
// Get the value
const value = this.EnvConfig[key]
// If the value is null, return
if (!value) {
console.error(`Configuration: Value for "${key}" is not defined`)
return
}
if (!value.startsWith('$VUE_APP_')) {
// value was already replaced, it seems we are in production (containerized).
return value
}
// value was not replaced, it seems we are in development.
const envName = value.substr(1) // Remove $ and get current value from process.env
const envValue = process.env[envName]
if (!envValue) {
console.error(`Configuration: Environment variable "${envName}" is not defined`)
return
}
return envValue
}
}
Create an entrypoint.sh
. With some modification, it would look like follows:
#!/bin/bash
function join_by { local IFS="$1"; shift; echo "$*"; }
# Find vue env vars
vars=$(env | grep VUE_APP_ | awk -F = '{print "$"$1}')
vars=$(join_by ',' $vars)
echo "Found variables $vars"
for file in /app/js/app.*;
do
echo "Processing $file ...";
# Use the existing JS file as template
cp $file $file.tmpl
envsubst "$vars" < $file.tmpl > $file
rm $file.tmpl
done
nginx -g 'daemon off;'
In your Dockerfile
, add a CMD
for running this entrypoint.sh
script above as a bootstrapping script during container creation. So that, every time you start a container, it will get the environment variables from the pod configuration and inject it to the Configuration class shown in Step 2.
# build stage
FROM node:lts-alpine as build-stage
# make the 'app' folder the current working directory
WORKDIR /app
# Copy package*.json and install dependencies in a separaate step to enable caching
COPY package*.json ./
RUN npm install
# copy project files and folders to the current working directory
COPY ./ .
# install dependencies and build app for production with minification
RUN npm run build
# Production stage
FROM nginx as production-stage
RUN mkdir /app
# copy 'dist' content from the previous stage i.e. build
COPY --from=build-stage /app/dist /app
# copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf
# Copy the bootstrapping script to inject environment-specific values and pass it as argument current to entrypoint
COPY entrypoint.sh entrypoint.sh
# Make the file executable
RUN chmod +x ./entrypoint.sh
CMD ["./entrypoint.sh"]
Finally, instead of process.env
use our wrapper configuration class like Configuration.value('envKey1')
. And voila!