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.productionfile based on.env.developmentfile via cmdgenerate. - Use cmd
replaceinDockerfileentrypoint in order to update environment variables during container recreations. - Copy and paste environment variables list from generated
.env.productiontodocker-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: