Have you ever tried to use environment variables in single page applications at browser runtime? There are several ways to do it, however almost all of them are too slow, too unsafe or have redundant complexity. So, spa-env
is fast, reliable and simple solution of this problem.
First of all there is a little note about environment variables in spa. Imagine that we have app.js
file with code below:
console.log(process.env.VITE_API_URL)
And there is .env
file as well:
VITE_API_URL=https://api.com/
As you've noticed we'll use vite
to build this application. After running yarn build
there will be built static file, which looks like this:
console.log("https://api.com/")
So, the main idea is that variables that refer on environment variables are replaced by static values at buildtime.
- Generate
.env.production
file based on.env.development
file via cmdgenerate
. - Use cmd
replace
inDockerfile
entrypoint in order to update environment variables during container recreations. - Copy and paste environment variables list from generated
.env.production
todocker-compose.yml
. - Fill environment variables values.
This command allows to generate .env.production
file filled with placeholders based on .env.development
file.
For example, some .env.development
file looks like this:
# server side variables
POSTGRES_CONN_STRING=postgres://username:password@prod.server:5432/database
# client side variables
API_URL=https://api.com/
SECRET_TOKEN=54321
Then run command below:
spa-env generate \
--workdir ./ \
--dotenv-dev .env.development \
--dotenv-prod .env.production \
--key-prefix NEXT_PUBLIC \
--placeholder-prefix PLACEHOLDER \
--enable-comments \
--log-level DEBUG
It will generate .env.production
that looks like this:
# This file was auto-generated by spa-env tool. Don't edit it manually!
# There is a full list of environment variables sorted alphabetically below.
# It includes client side variables as well as server side variables.
# Just copy this list and paste it to app service environment in docker-compose.yml file.
#
# API_URL
# SECRET_TOKEN
# POSTGRES_CONN_STRING
# env -> API_URL
# src -> process.env.NEXT_PUBLIC_API_URL
NEXT_PUBLIC_API_URL=PLACEHOLDER_API_URL
# env -> SECRET_TOKEN
# src -> process.env.NEXT_PUBLIC_SECRET_TOKEN
NEXT_PUBLIC_SECRET_TOKEN=PLACEHOLDER_SECRET_TOKEN
Further this file could be used in combination with command replace
in order to use runtime environment variables in spa.
Common Dockerfile
for nextjs apps looks like this:
# deps stage
...
# build stage
...
# runtime stage
...
# run app
CMD ["node", "server.js"]
All spa-env
usage could be injected in runtime stage in Dockerfile
of target spa. So, Dockerfile
for nextjs app turns into this:
# deps stage
...
# build stage
...
# runtime stage
...
# download binary from official image
COPY --from=tcaty/spa-env /spa-env /spa-env
# run binary
ENTRYPOINT [ \
"/spa-env", "replace", \
"--workdir", "/app", \
"--dotenv", ".env.production", \
"--key-prefix", "NEXT_PUBLIC", \
"--placeholder-prefix", "PLACEHOLDER", \
"--cmd", "node server.js", \
"--log-level", "DEBUG" \
]
Further just copy environment variables list from generated .env.production
file and paste it to docker-compose.yml
file. For example, it could looks like this:
...
services:
nextjs:
...
environment:
API_URL: "https://myapi.com"
SECRET_TOKEN: "fksdilall990fas"
POSTGRES_CONN_STRING: "postgres://username:password@mydbhost:5432/database"
There are two available examples in examples
folder: