21

I'm pretty new to React, and exploring Azure in general as well. I've gotten an ERP background, but that background did include using tools like VSTS and CI/CD. I've heavily relied upon using the 'libraries' in VSTS to specify variables per environment, and then specifying these upon deployment.

But! I've been reading around on the internet, and playing with settings, but to my understanding, I can only 'embed' parameters in the actual code that is generated by NPM. This would basically mean that I'd need to create a seperate build per environment, which I'm not used to. I've always been tought (and tell others) that what you ship to production, should be exactly the same as what has been on pre-prod, or staging, or ... . Is there really no other way to use environment variables? I was thinking of using the Application Settings in Azure App Service, but I can't get them to even pop up in the console. The libraries in VSTS, haven't found how to use these in my deployment either, as there's just one step.

And reading the docs at https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#adding-custom-environment-variables doesn't make me feel comfortable putting .env files in source control either. I even tried the approach of putting {process.env.NODE_ENV} in my code, but in Azure it just shows up as 'Development', while I even do npm run build (which should be production)...

So, I'm a bit lost here! How can I use environment variables specified in Azure App Service, in my React app?

Thanks!

Mortana
  • 1,176
  • 3
  • 12
  • 27
  • 3
    You can put them in the start of your script as well, e.g. `"scripts": { "start": "cross-env REACT_APP_TEST=wow react-scripts start" }` – Tholle Jul 12 '18 at 19:44
  • 1
    But that gets messy very fast if you have many variables right? – Mortana Jul 13 '18 at 05:22

9 Answers9

9

The Good Options

I had this problem as well you can customize which env variables are used by using different build scripts for your envs. Found this CRA documentation https://create-react-app.dev/docs/deployment/#customizing-environment-variables-for-arbitrary-build-environments

You can also set your variables in your YAML. https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#set-variables-in-pipeline

But what if I need a single build?

I haven't solved this yet if you are using a single build and release stages for different envs (dev, staging, prod). Since everything is built React has whatever env variables you provided at build time. Alternatives I've considered:

  1. Separating react build from .NET build, so that you could do this as for each deploy
  2. Define all env variables and append eg REACT_APP_SOME_KEY_ then based on subdomain pick specific env eg https://dev.yoursite.com https://yoursite.com , but this option seems non-canonical.
  3. Might be a limitation of React needing to build for every environment. Accept that you need separate builds.
Jonathon H.
  • 141
  • 3
  • 6
  • Everyone else talks about build time variable. You are talking about run time variables. Build time variables are useless, if you want to quickly change an api key(assuming it is in azure) and test the app without redeploying. There are no real solutions yet. – int-i Feb 10 '22 at 18:06
6

Add the venerable directly to build pipeline Variables. This will add to the Azure environment variable and the app can use it enter image description here

Shinoy Babu
  • 1,373
  • 1
  • 11
  • 11
  • as simple as this – emamones Apr 22 '20 at 10:55
  • It's not though. What if your infrastructure is contained within an ARM template for an environment that hasn't been created yet? Therefore you won't know what the value of REACT_APP_API_URL is until a release has been run. You can manually attempt to keep your build and release pipelines and variables in sync but this defeats the object of infrastructure as code. – Konzy262 Aug 28 '20 at 10:38
  • 1
    I did create those variables, but still the `process.env.CLIENT_ID`, for example in my case, when the pipeline is complete and deployed, still getting error that the client id does not exist, any extra step to make it work? – ddieppa Mar 10 '21 at 19:27
  • 1
    @ddieppa I think it needs to be process.env.REACT_APP_CLIENT_ID – Cathal Mac Donnacha May 19 '21 at 15:19
  • @ddieppa, were you able to solve this issue? – Darshani Jayasekara Aug 30 '21 at 07:22
  • 2
    This works. I did find out that if you add a variable in regular case it will convert to UPPERCASE. Note that any environmental variables besides NODE_ENV must be prefixed with REACT_APP_. @CathalMacDonnacha's comment above this is exactly correct. – St.G Jan 13 '22 at 23:33
  • You will never be able to swap in production. I personally hate the pipeline variables – Nick Turner Jan 19 '22 at 23:31
3

When you do the deployment using VSTS to Azure, you can give your environment variables in the build pipeline which will automatically include it in the ReactJS project. enter image description here

Shinoy Babu
  • 1,373
  • 1
  • 11
  • 11
3

now the end of 2019 and I am still facing the issue with env variables in nodeJs and azure devops.

I didn't find a solution, but I use a workaround. I use pseudo "env var".

I created "env.json" file with the same structure as ".env" file in the project's root. Put this file to ".gitignore" file. Imported this file explicitly to files where I need to use env var. Use it as regular object, instead of process.env.***

Example:

we have ".env", that we need to replace:

REACT_APP_SOMW_KEY=KEY

The next steps for project itself are:

Create "env.json":

{"REACT_APP_SOMW_KEY":"KEY"}

Add it to ".gitignore".

In case of using typescript add the next settings to tsconfig.json:

 "resolveJsonModule": true,

In files where process.env.REACT_APP_SOMW_KEY are located change process.env.REACT_APP_SOMW_KEY to config.REACT_APP_SOMW_KEY and add const config = require("../pathTo/env.json") as a import module in the begginning.

In case of typescript yo can also create interface just to have autocomplete:

export interface IEnvConfig{
  REACT_APP_SOMW_KEY?: string;
}   
const config: IEnvConfig = require("../pathTo/env.json");

The result will be something like this:

const reactSomeKey = /*process.env.REACT_APP_SOMW_KEY*/ config.REACT_APP_SOMW_KEY;

Next steps for Azure DevOps:

Add your keys to azure "key vault" or "variables".

In the CI pipeline before the step of building the project you can set the PowerShell task, which will create the "env.json" file. The same as we should create ".env" file locally since we made git clone with the hidden ".env" file. I put yml task here (in the end you can see 2 debug commands just to be sure that file is created and exist in a project):

- powershell: |
   New-Item -Path $(System.DefaultWorkingDirectory) -Name "env.json" -Force -Value @'
   {
   "REACT_APP_SOMW_KEY": "$(REACT_APP_SOMW_KEY)",
   }
   '@
   Get-Content -Path $(System.DefaultWorkingDirectory)\env.json
   Get-ChildItem -Path $(System.DefaultWorkingDirectory)
  displayName: 'Create "env.json" file'

Outcome: you have almost the same flow with json object keys as you are usually using with ".env". Also you can have both ".env" and "env.json" in the project.

Ruslan Korkin
  • 2,460
  • 1
  • 24
  • 20
  • For the Azure DevOps deployment, I did almost exactly the same thing. However, I'm serving up my app from a static website from a storage container. I changed this so I generate a config.js file and put it in the webroot. In this file, I create an object on the window like so: `window.appSettings = { x: "asdf",... }` Then I can refer to this from other parts in my codebase. It seems to at least solve my problem of not having to rebuild my app to deploy it for the different environments. – Richard Mar 10 '20 at 00:22
  • The problem I have with this approach like the other answers is that you are hard coding your values into the build. While ok for a small shop, in large shops App Services change, dns changhe, other settings have admins applying biceps. So the admin applies the bicep, he has 100+ app services. Someone deploys your build and he loses everything. Also you will not be able to swap PreProd to Prod and slot settings are a better use. The marked answer shows an example of this – Nick Turner Jan 19 '22 at 23:37
2

All proposed solutions are way too complex because others already have solved this problem during the package and build process.

To deploy this to azure 2 things have to be done. First remove the .ignore rule that excludes the .env* files. NOTE: ASSUMED you do not put secrets here! Most of the config in the .env file is visible online anyway, during the auth-flow. So, why panic about this file in git? Espially in a private Git I don't see any problem for those .env files.

So, I have .env.dev and a .env.prod... this contains e.g.

REACT_APP_AUTH_URL=https://auth.myid4.info
REACT_APP_ISSUER=https://auth.myid4.info
REACT_APP_IDENTITY_CLIENT_ID=myclientid
REACT_APP_REDIRECT_URL=https://myapp.info/signin-oidc
REACT_APP_AUDIENCE=
REACT_APP_SCOPE=openid profile email roles mysuperapi
REACT_APP_SILENT_REDIRECT_URL=https://myapp.info/silent-renew
REACT_APP_LOGOFF_REDIRECT_URL=https://myapp.info/logout
API_URL=/

the following must be done. npm i --save-dev env-cmd

now, modify in package.json like this. You may have some others, but essentially, add just the correct .env for your environment

env-cmd -f .env.prod

so in my case in package.json

"start": "env-cmd -f .env.dev rimraf ./build && react-scripts start",
"build": "env-cmd -f .env.prod react-scripts build"

Now, I deployed my react JS to azure. I use, FYI, the .NET Core Spa feature.

Egbert Nierop
  • 1,668
  • 12
  • 16
1

This route only applicable if you are using Azure DevOps.

  • Azure DevOps has Section in Pipeline called Library.
  • Create a new Variable Group and add your env variables.
  • Associate last create Variable group to your build process.

Also remember to name your env variable starting with REACT_APP_

Dhruv Joshi
  • 201
  • 2
  • 3
1

I used a YAML build and wrote the variable to the .env file. The package I was using to do the transforms in reactjs was dotenv version 8.2.0

So here is my YAML build file, with tasks added to accomplish this

variables:
- group: myvariablegroup

trigger:
  batch: true
  branches:
    include:
      - develop
      - release/*

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: dev 
  condition: eq(variables['build.sourceBranch'], 'refs/heads/develop')
  jobs:
  - job: DevelopmentDelpoyment
    steps:

    - task: CmdLine@2
      inputs:
        script: 'echo APP_WEB_API = $(myvariable-dev) > Web/.env' 
      displayName: 'Setting environment variables'


    - script: |
        cd Web
        npm install
        npm run build
      displayName: 'npm install and build'


- stage: prod 
  condition: eq(variables['build.sourceBranch'], 'refs/heads/master')
  jobs:
  - job: ProductionDelpoyment
    steps:

    - task: CmdLine@2
      inputs:
        script: 'echo APP_WEB_API = $(myvariable-prod) > Web/.env' 
      displayName: 'Setting environment variables'

    - script: |
        cd Web
        npm install
        npm run build
      displayName: 'npm install and build'
Joshua Duxbury
  • 4,430
  • 4
  • 29
  • 49
0

It's not exactly what you are looking for, but maybe this is an alternative solution for your problem (it substitutes the process-env.x into real values during the build step):

https://github.com/babel/minify/tree/master/packages/babel-plugin-transform-inline-environment-variables

Philip Claren
  • 2,232
  • 2
  • 20
  • 29
-2

As an update, it's a bit different then my original approach, but I've gone through the route of using DotEnv and thus using .env files, which I will generate on the fly in VSTS, using the library variables, and thus NOT storing them in source control.

To use DotEnv, I updated the webpack.config; const Dotenv = require('dotenv-webpack');

module.exports = {
    ...
    plugins: [
        new Dotenv()
    ],

Then basically, I created a .env file containing my parameters

MD_API_URL=http://localhost:7623/api/

And to be able to consume them in my TSX files I just use process.env;

static getCustomer(id) {
    return fetch(process.env.MD_API_URL + 'customers/' + id, { mode: 'cors' })
        .then(response => {
        return response.json();
    }).catch(error => {
        return error;
    });
}
Mortana
  • 1,176
  • 3
  • 12
  • 27