React configuration file for post deployment settings
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:
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 atwindow.ENV.ENVIRONMENT
)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
underpublic
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 staticpublic
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 usingnginx
anyway to serve my static react app, I have absolutely no trouble doing this.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 additionalconfig.js
as it will be available under thebuild
directory after compiled (due to it being placed underpublic
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 .
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
andsubPath
.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
andsubPath
parameters are necessary to replace a file under a directory that already has some files existing in it. IfsubPath
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.
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.