Search code examples
environment-variablescreate-react-app

Create React App inject environment variable


I'm new to react and started with the create-react-app template. I am developing a SPA, that is consuming a REST API.

For developing purposes, i need to authenticate against this API using OAUTH2 (Access Token).

In the production environment, i don't need to authenticate (as it runs on the same machine).

In order to get this access token in the dev environment, I need to make a POST request to the authentication server (with client_id and client_secret) and then I receive the access token which I need for further request.

As the authentication server does not support cors, I cannot do this post request within the react app.

My solution was to write a node script, that does this post request and inject the token to the client App using environment variables.

In the package.json (I did an eject) I inserted this script (gettoken):

"start": "npm-run-all -p watch-css gettoken-js start-js"

In the gettoken.js file I make the post request for getting the access token and set (in the callback function):

process.env.REACT_APP_CRM_API_ACCESS_TOKEN = response.access_token;

Now I want to access this variable in the react app - but here process.env.REACT_APP_CRM_API_ACCESS_TOKEN is always undefinied.

What am I doing wrong? Is there another way to inject the access_token to the client app?

Here is the getToken script:

var request = require('request');

const endpoint = "https://xxxx/oauth2/token";
const resource = "https://xxxyyy.com";

 const params =  [
  {
    name: 'userName',
    value: '[email protected]'
  },
  {
    name: 'password',
    value: '123'
  },
  {
    name: 'grant_type',
    value: 'password'
  },
  {
    name: 'client_secret',
    value: '11231'
  },
  {
    name: 'client_id',
    value: '123'
  },
  {
    name: 'resource',
    value: resource
  }
];

const encodedParams= Object.keys(params).map((key) => {
  return params[key].name + '=' + encodeURIComponent(params[key].value);
}).join('&');

request(
    {
        method: 'POST',
        uri: endpoint,
        headers: [
            {
              name: 'content-type',
              value: 'application/x-www-form-urlencoded'
            }
          ],
          body: encodedParams + "&"
        }
    ,
    function (error, response, body) {

      //throw new Error("asdfasdf");
        if (error)
        {
            console.log('error', "ERROR GETTING ACCESS TOKEN FROM API: " + error);
        }
        else
        {
          let response = JSON.parse(body);
          process.env.REACT_APP_CRM_API_ACCESS_TOKEN = response.access_token;
          process.env.REACT_APP_CRM_API_ENDPOINT = resource;
          console.log(process.env.REACT_APP_CRM_API_ACCESS_TOKEN);
        }
    }
);


Solution

  • You're bumping into a common issue with configuration in a Create React App.

    The REACT_APP_ prefix is used at build time. Are you building after you put REACT_APP_CRM_API_ACCESS_TOKEN into the env? If not, the app doesn't have them.

    If you're happy to have the token in the JS bundle then go with that.

    Depending on how you plan to promote your build through various environments, you'll likely bump into other issues.

    Here's one possible pipeline and the issue you'll bump into.

    You have staging and production.

    You build your app and end up with staging env variables built into the bundle.

    You promote that bundle to production and the staging env vars are still in the bundle.

    Two ways around it:

    1. Rebuild on production so the prod vars are put into the bundle.
    2. Use the web server to inject the vars from the environment into one of the files in your SPA.

    I've gone through this 4 times now and tweaked my solution slightly each time. Each time was on an ejected CRA app and each solution wasn't very DRY.

    I'm now trying to solve it for a non ejected CRA and again, trying to find a DRY solution is proving tricky.

    Will update this answer if I find a nicer way.


    Edit: Because you have ejected the app, you can change config/env.js to do whatever you need. Including the way the REACT_APP_ prefix works.