Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React configuration file for post deployment settings

I've built a reactjs site and trying to get it to be deployable.

right now all configuration is done via a config.js which is imported into all modules.

But when I build the app, this gets compiled into the deployment js and isn't configurable.

I want to have a separate file which a sys admin can configure various settings specific to their enviroment, such as the API end points (the app may not be running on the same server as the backend, and there will not be any access to DNS).

Is there a way to do this in react? I do not wish to use 3rd party libraries for this either.

like image 301
Space Bear Avatar asked Aug 02 '18 12:08

Space Bear


People also ask

Which is the main configuration file for a React application?

You can store the configuration data in a JSON file. Just like a component, the JSON file can be loaded using the import statement. And then you can use the data as any other JavaScript object. For example, configData.

Why we use .env file in React?

env file in our react js project. If you are using some external APIs for data you must use the . env file to store your sensitive credentials like API keys. Environment variables can also help us to store our API link in one location so that we don't have to change the link in each file manually.


Video Answer


3 Answers

Not sure about the linux approach, but I am using create-react-app (cra), Docker & kubernetes a lot and initially struggled with similar problem. I was then inspired by https://github.com/inloop/cra-docker and was able to find a solution for the configuration file problem with create-react-app at runtime in both Docker and kubernetes. Below are the steps in my solution:

  1. Have your custom configuration (eg: config.js) ready. The contents inside your config file should look like this:

     window.ENV = {
         "ENVIRONMENT":"stg",
         ...other key-value configuration as you'll need
     }
    

    Your configurations will be accessible anywhere in your code by accessing window.ENV.your_configuration_key (eg: the ENVIRONMENT value above is available at window.ENV.ENVIRONMENT)

  2. Under public directory, edit the index.html and add

     <script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
    

    in your head before the body. And put config.js under public directory.

    Your goal in solving the external configuration for cra is that you want to put your config.js file outside source directory and put it under the static public directory. If you put the config under source directory, the config will get compiled during build time so you won't able to change the config during runtime easily (well, templating works too but it's not ideal to me). Do note that serving files from static directory require a server, but since I'm already using nginx anyway to serve my static react app, I have absolutely no trouble doing this.

  3. Since I was already using nginx to serve the static files for my react app, I don't need to make changes to my Dockerfile to cater for additional config.js as it will be available under the build directory after compiled (due to it being placed under public directory). My Dockerfile looks something like this:

     # Step 1: Build static react app
     FROM node AS builder
    
     # Define working directory and copy source
     WORKDIR /app
    
     COPY . .
    
     # Install dependencies and build whatever you have to build 
     RUN yarn install && yarn build
    
     # Step 2: Run image
     FROM nginx
    
     COPY --from=builder /app/build /usr/share/nginx/html
     RUN rm /etc/nginx/conf.d/default.conf
    
     COPY nginx.conf /etc/nginx # this step is not required, only when you have custom nginx configuration
    

    Then afterwards build your docker image:
    docker build -t [your-docker-image]:latest .

  4. Last but definitely not least, you'll want to replace your config.js file during runtime. This can now be done easily.

    If you're running using docker, you can replace files by using the -v command. So your docker runtime command should look something similar to this:

     docker run --name [app-container-name] -d -p [host_port]:80 \
         -v [path_to_config.js_file_in_certain_environment]:/usr/share/nginx/html/config.js \
     [your-docker-image]
    

    If you're running using kubernetes, you can replace files in an existing directory under your container by using configMap, volume, volumeMounts and subPath.

    • First put your config.js under k8s ConfigMap:

      kubectl create configmap [k8s_config_name] --from-file=config.js=[path_to_config.js_file_in_certain_environment]

    • Mount your configMap in your k8s deployment:

      containers:        
        ...
        volumeMounts:
        - name: [k8s_config_name_volume]
          readOnly: true
          mountPath: "/usr/share/nginx/html/config.js"
          subPath: "config.js"
      
      volumes:
        - name: [k8s_config_name_volume]
          configMap:
            name: [k8s_config_name]
      

    Note that both mountPath and subPath parameters are necessary to replace a file under a directory that already has some files existing in it. If subPath is omitted during volume mount to an existing directory which already contains some files the result is unfavourable in our case cos it will override the existing directory by copying the new file into the directory but removing all other previously existing files.

like image 183
dekauliya Avatar answered Oct 13 '22 02:10

dekauliya


I've managed to hack together a solution.

in the public folder 'config.js'

var config = {
      x: 'y',
};

Next wrap the ReactDOM.render (App/index.js in a fucntion like so

window.RenderApp = (config) => {
   ReactDOM.render(<App _config={config}/>, document.getElementById('root'));
}

In the index.html add these lines, the window.RenderApp HAS to be at the end, because it relies on bundle.js being imported which is auto added by react and has a random name in production.

</html>
...
<head>
...
<script type="text/javascript" src="%PUBLIC_URL%/config.js"></script>
...
</head>
...
<body>
...
</body>
<script>
   window.RenderApp(config);
</script>
</html>

lastly to use the config variables in your App.js or what ever you called it

...
constructor(props) {
    super(props)
    console.log(this.props._config)
    this.state = {
       ....
       config: this.props._config,
   }
}
...

I found you have to set config to a state variables or else it will randomly throw undefined for the object, now just pass config down the hierarchy to use in your code.

like image 11
Space Bear Avatar answered Oct 13 '22 04:10

Space Bear


We had a similar use case to - 'build once, deploy anywhere'. The following code seems to work for us:

There is config.js file in the public directory with the following code: const API_URL = '<API_URL>';

index.html file contains the following:

<script src="/config.js"></script>
<script>
(function (global) {
  var config = {
    API_URL
  };

  global.app = {
    env: config
  };
})(window);

This adds the variables from config.js into the global scope and can be accessed anywhere in the source code as : const API_ROOT = window.app && window.app.env.API_URL;.

like image 8
Princi Ya Avatar answered Oct 13 '22 03:10

Princi Ya