Artigo original: https://www.freecodecamp.org/news/how-to-implement-runtime-environment-variables-with-create-react-app-docker-and-nginx-7f9d42a91d70/
Escrito por: Krunoslav Banovac
Existem muitas maneiras de configurar sua aplicação em React. Vamos usar uma abordagem que respeita a metodologia Twelve-Factor App. Isso significa que ela exige a reconfiguração durante o tempo de execução. Assim, não é exigida uma build por ambiente.
O que queremos conseguir?
Queremos poder executar nossa aplicação em React como um contêiner do Docker criado uma única vez. Ele poderá ser executado em qualquer lugar, sendo configurável durante o tempo de execução. O resultado deve ser um contêiner leve e com bom desempenho, que serve nossa aplicação do React como um conteúdo estático, o que conseguiremos usando o Ngnix Alpine. Nossa aplicação deve permitir a configuração no arquivo docker-compose, assim:
version: "3.2"
services:
my-react-app:
image: my-react-app
ports:
- "3000:80"
environment:
- "API_URL=https://production.example.com"
Devemos poder configurar nossa aplicação em React usando a flag -e
(de variáveis de ambiente) ao usar o comando Docker run
.
À primeira vista, essa abordagem pode parecer trazer um benefício muito pequeno com relação ao trabalho adicional que ele exige para a configuração inicial. Porém, assim que a configuração está pronta, as configurações específicas do ambiente e sua implantação serão muito mais fáceis de tratar. Assim, para aqueles que buscarem ambientes dinâmicos ou que usarem sistemas de orquestração, essa abordagem é, definitivamente, algo para se pensar a respeito.
O problema
Para começar, deve ficar claro que não existe algo como variáveis de ambiente dentro do ambiente do navegador. Seja qual for a solução que usarmos hoje em dia, ela será apenas uma abstração falsa.
Porém, você pode estar se perguntando, e quanto aos arquivos .env
e as variáveis prefixadas com REACT_APP
que vêm diretamente da documentação (texto em inglês)? Mesmo dentro do código fonte, elas são usadas como process.env
, assim como usamos as variáveis de ambiente dentro do Node.js.
Na verdade, o objeto process
não existe dentro do ambiente do navegador. Ele é específico do Node. O create-react-app, por padrão, não faz renderização do lado do servidor. Ele não pode injetar variáveis de ambiente quando o conteúdo é servido (como faz o Next.js). Durante a transpilação, o processo do Webpack substitui todas as ocorrências de process.env
com um valor de string que foi fornecido. Isso significa que ele somente pode ser configurado durante o tempo de criação (build).
Solução
O momento específico quanto ainda é possível injetar variáveis de ambiente ocorre quando iniciamos nosso contêiner. Então, podemos ler as variáveis de ambiente dentro do contêiner. Podemos escrevê-las em um arquivo que pode ser servido por meio do Nginx (que também serve nossa aplicação em React). Elas são importadas em nossa aplicação usando a tag <script>
na seção head do index.html
. Desse modo, nesse momento, executamos um script do bash que cria um arquivo JavaScript com variáveis de ambiente atribuídas como propriedades do objeto global window
. Injetadas de modo a estarem disponíveis globalmente dentro de nossa aplicação da maneira que o navegador as recebe.

Guia passo a passo
Vamos iniciar com um projeto do create-react-app
simples e criar um arquivo .env
com a primeira variável de ambiente que queremos expor.
# Gerar a aplicação do React
create-react-app cra-runtime-environment-variables
cd cra-runtime-environment-variables
# Criar as variáveis de ambiente padrão que queremos usar
touch .env
echo "API_URL=https//default.dev.api.com" >> .env
Em seguida, vamos escrever um script do bash pequeno, que lerá o arquivo .env
e extrairá as variáveis de ambiente que serão escritas no arquivos. Se você definir uma variável de ambiente dentro do contêiner, seu valor será usado. Do contrário, ela voltará ao valor padrão do arquivo .env
. Será criado um arquivo JavaScript que coloca os valores das variáveis de ambiente como um objeto, que é atribuído como uma propriedade do objeto window
.
#!/bin/bash
# Recriar o arquivo de configuração
rm -rf ./env-config.js
touch ./env-config.js
# Adicionar a atribuição
echo "window._env_ = {" >> ./env-config.js
# Ler cada linha no arquivo .env
# Cada linha representa pares chave-valor
while read -r line || [[ -n "$line" ]];
do
# Dividir as variáveis env pelo caractere `=`
if printf '%s\n' "$line" | grep -q -e '='; then
varname=$(printf '%s\n' "$line" | sed -e 's/=.*//')
varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//')
fi
# Ler os valores da variável atual se existir como uma variável de ambiente
value=$(printf '%s\n' "${!varname}")
# Caso contrário, usar o valor do arquivo .env
[[ -z $value ]] && value=${varvalue}
# Associar a propriedade de configuração ao arquivo JS
echo " $varname: \"$value\"," >> ./env-config.js
done < .env
echo "}" >> ./env-config.js
Precisamos adicionar a linha abaixo ao elemento <head>
de index.html
, que importa o arquivo criado por nosso script do bash.
<script src="%PUBLIC_URL%/env-config.js"></script>
Vamos exibir nossa variável de ambiente dentro da aplicação:
<p>API_URL: {window._env_.API_URL}</p>
Desenvolvimento
Durante o desenvolvimento, se não usarmos o Docker, podemos executar o script do bash pelo executor npm script
modificando o package.json
:
"scripts": {
"dev": "chmod +x ./env.sh && ./env.sh && cp env-config.js ./public/ && react-scripts start",
"test": "react-scripts test",
"eject": "react-scripts eject",
"build": "react-scripts build'"
},
Se executarmos yarn dev
, deveremos ver o resultado, assim:

Existem duas maneiras de reconfigurar as variáveis de ambiente dentro de dev. Mude o valor padrão dentro do arquivo .env
ou sobrescreva os padrões executando o comando yarn dev
com as variáveis de ambiente pré-anexadas:
API_URL=https://my.new.dev.api.com yarn dev

Por fim, editamos .gitignore
, para que possamos excluir as configurações de ambiente do código fonte:
# Arquivos env temporários
/public/env-config.js
env-config.js
Quanto à variável de ambiente, estamos resolvidos! Já estamos a meio caminho. Não há uma diferença muito grande nesse momento em comparação ao que oferecia o create-react-app por padrão para o ambiente de desenvolvimento. O verdadeiro potencial dessa abordagem aparece de verdade em produção.
Produção
Agora, criaremos a configuração mínima do Nginx de modo a podermos criar uma imagem otimizada que sirva a aplicação já pronta para a produção.
# Criar o diretório para a configuração do Ngnix
mkdir -p conf/conf.d
touch conf/conf.d/default.conf conf/conf.d/gzip.conf
O arquivo de configuração principal deve ter essa aparência:
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
expires -1; # Defina com um valor diferente, dependendo de seus requisitos padrão
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Também é útil habilitar a compressão gzip para que os ativos de nossa aplicação sejam mais leves durante a transição de rede:
gzip on;
gzip_http_version 1.0;
gzip_comp_level 5; # 1-9
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
# MIME-types
gzip_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/svg+xml
image/x-icon
text/css
text/plain
text/x-component;
Agora que nossa configuração do Nginx está pronta, podemos finalmente criar os arquivos Dockerfile e docker-compose:
touch Dockerfile docker-compose.yml
Inicialmente, usamos a imagem node:alpine
para criar uma build de produção otimizada de nossa aplicação. Em seguida, criamos uma imagem de tempo de execução com base no nginx:alpine
.
# => Contêiner da build
FROM node:alpine as builder
WORKDIR /app
COPY package.json .
COPY yarn.lock .
RUN yarn
COPY . .
RUN yarn build
# => Executar o contêiner
FROM nginx:1.15.2-alpine
# Config do Nginx
RUN rm -rf /etc/nginx/conf.d
COPY conf /etc/nginx
# Build estática
COPY --from=builder /app/build /usr/share/nginx/html/
# Exposição da porta padrão
EXPOSE 80
# Copiar o arquivo .env e o script do shell no contêiner
WORKDIR /usr/share/nginx/html
COPY ./env.sh .
COPY .env .
# Adicionar o bash
RUN apk add --no-cache bash
# Tornar nosso script do shell executável
RUN chmod +x env.sh
# Iniciar o servidor do Nginx
CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]
Agora, nosso contêiner está pronto. Podemos fazer todas as ações padrão com ele. Podemos criar um contêiner, executá-lo com as configurações em linha e enviá-las por push a um repositório fornecido por serviços como o Dockerhub.
docker build . -t kunokdev/cra-runtime-environment-variables
docker run -p 3000:80 -e API_URL=https://staging.api.com -t kunokdev/cra-runtime-environment-variables
docker push -t kunokdev/cra-runtime-environment-variables
O comando docker run
acima deve ter como resultado uma aplicação assim:

Por fim, vamos criar nosso arquivo docker-compose. Você geralmente terá arquivos docker-compose diferentes, dependendo do ambiente, e usará a flag -f
para selecionar o arquivo a ser usado.
version: "3.2"
services:
cra-runtime-environment-variables:
image: kunokdev/cra-runtime-environment-variables
ports:
- "5000:80"
environment:
- "API_URL=production.example.com"
Se usarmos docker-compose up
, deveremos ver o seguinte resultado:

Ótimo! Alcançamos nosso objetivo. Podemos reconfigurar nossa aplicação facilmente nos ambientes de desenvolvimento e de produção de modo bastante conveniente. Podemos finalmente criar uma vez e executar em qualquer lugar!
Se ficar travado em algum momento ou se tiver outras ideias para dar, acesse o código fonte no GitHub.
Próximos passos
A implementação atual do script do shell imprimirá todas as variáveis incluídas com o arquivo .env
. Na maioria das vezes, não queremos expor todas elas. Você pode implementar filtros para as variáveis que não quiser expor usando prefixos ou alguma técnica semelhante.
Soluções alternativas
Como vimos acima, a configuração do tempo de criação (da build) satisfará a maioria dos casos de uso. Você pode confiar na abordagem padrão usando o arquivo .env
por ambiente, criar um contêiner para cada ambiente e injetar os valores pelas variáveis de ambiente fornecidas pelo Webpack do create-react-app.
Você também pode dar uma olhada nessa issue do repositório do GitHub do create-react-app, a qual trata desse problema. Neste momento, já deve haver mais publicações e issues que tratem desse tópico. Cada uma delas oferecerá uma solução semelhante a que vemos acima. A decisão é sua sobre como desejará implementar os detalhes específicos. Você pode usar o Node.js para servir sua aplicação, o que significa que você também substituirá os scripts do shell por scripts do Node.js. Observe que o Nginx é mais conveniente para servir o conteúdo estático.
Se tiver perguntas ou se quiser enviar comentários, fique à vontade de abrir uma issue no GitHub. Como opção, você também pode seguir o autor para ver outras outras publicações dele relacionadas às tecnologias da web.