Configurable frontend apps with docker and env-serve
Recently everyone speaks about Docker. There is a lot of articles on the web which in brief saying how docker helps scaling, sharing and running backend apps, but what about frontend?
First big disadvantage and difference between backend and frontend is that backend, even after build to some executable, is still application. You can run it and control from outside by parameters or environment variables passed on the initial phase while frontend application is only a bunch of static files served by other app, mostly server like Nginx or Apache.
For better understanding what problem we discuss let’s say you put a variable color
in index.js
file which holds a primary color for UI components. When you have single version of app this is not a problem, you can change variable and it’ll affect whole interface in every app version. But it becomes a problem when app could be running at the same time in many color variants.
Let’s assume you have “Client A” and “Client B”. Client A uses green variant of your app, but here comes Client B and says “App looks great, but hey! My logo is red, I want to have your tool in my company colors!”.
Client A is good with green, Client B wants red. At this moment, for the purposes of experiment, we assuming that app will be served on two different ports for each client and client can’t switch between color schemes dynamically. In this situation you may think “Okay, we’ll build conditionally basing on env variables” — and it’s great move! Let’s do this, something like: EXPORT PRIMARY_COLOR=red; node scripts/build.js
Then in your build.js
file:
Seems legit, it works. Now you can build your frontend app in two different color variants. But there are also disadvantages of above solution:
- It can’t be changed without rebuild whole app
- when it comes to run app in docker, you also need to install all dependencies and after that again rebuild app , in brief — it takes a lot of time to build version with only one, small change in color variable
above problems are aftermath of mentioned at the beginning difference: frontend apps are only bunch of static files, you can not communicate with them at runtime like with other apps.
According to this problem I wrote small server called env-serve
which helps you configure frontend apps on runtime, it may be useful especially when you work with docker.
Example app with docker
Full code available on GitHub: https://github.com/dawiidio/env-serve-example
For the beginning let’s take a look on the Dockerfile:
What are we doing here:
- declare custom env vars
- copy app source code from host to container
- install dependencies, env-serve then build app
- expose app port (default 3000)
- change directory do
app/src
. In real app it would be directory with output from bundler eg.app/dist
- and set
env-serve
as entrypoint
to work with env-serve you need also entry file, eg. index.html
. One important thing to remember about entry file — it must contains initial shape of config object, like:
<script>
window.appConfig = {
"CLIENT_NAME": "Client A",
"PRIMARY_COLOR": "green"
};
</script>
env-serve will be looking for window.appConfig
in entry file and then will try match it’s keys with env vars, if it finds for example CLIENT_NAME
then replace it’s initial value with value provided in the env var. If eg.PRIMARY_COLOR
wasn’t provided then it stays untouched.
Above configuration gives you ability to build your frontend once, and then configure on runtime, just like backend apps. Thanks to this you can run many variants of your app faster.
# build it once
docker build -t frontend:latest .# then run many different variants in seconds without rebuild
docker run -p 3000:3000 -e CLIENT_NAME="Client A" frontend:latestdocker run -p 3001:3000 -e CLIENT_NAME="Client B" frontend:latestdocker run -p 3002:3000 -e CLIENT_NAME="Client C" frontend:latest
I wrote a small docker-compose.yaml
to put both version together and run by single docker-compose up
command
I have a few lines of JS in my index.html
to pull informations from data passed by env-serve
to my initial config declared also in the same file. Here’s content of it
How you can see above, I’ve declared shape of my config, then I pulls needed informations from it and basing on them customize interface. Simple :)
After run docker-compose up
I can see something like this in console:
And this the final result in my browser, after open localhost on declared in docker-compose.yaml
ports.
When you shouldn’t use env-serve?
There are some cases, which still should be done on the build phase. For example: if client A asks you for feature X but client B shouldn’t see it, probably because he don’t pay for it, you should add some env variable and join feature code conditionally basing on it, also you shouldn’t put any sensitive data to config (eg. passwords) because anyone can see it.
Summary
We discussed main difference between built backend and frontend, how it affects further work with Docker and in general how it may complicate process of adjusting and delivering app for many different clients . We also tried solution called env-serve which addresses some of the problems.
If you have any problems or suggestions please let me know here or on twitter https://twitter.com/Caridina_multid :)