I'm trying to find an elegant solution to something which should be really simple. I am working on a React app using create-react-app and I'm trying to find an easy way to substitute variables (eg. API Address) when deploying code to different environments in Azure using Azure DevOps.
So far I've been using a .env and .env.production files to store the variables, which work great as long as we only have one environment. But as I'm planning to have three environments in total (test, qa and production) I have to find a better solution.
One approach would be to substitute variables in the npm build stage of our CI build. This approach would work, but we are not interested in creating one build per environment.
I've tried to use Application Settings in Azure, and creating my own Environment Variables, but these variables simply aren't available using process.env in my React code.
Is there a way to easily inject these variables when releasing to Azure? Alternatively can we configure these in Azure somehow?
Or is there another solution?
Complex applications developed with react, vue, angular or other javascript based frameworks has the same problem or requirement as backend applications (java, nodejs, python, etc): How read configurations?
Here I will list some some approaches to work with configurations for javascritpt frameworks, from simple to manged solutions. Some of them are used for backend applications.
#1 Global variables
As we are talking about javascript frameworks, just create global var at the startup of your application and this will be available for all your scripts:
<html>
<header>
<meta charset="utf-8">
<title>This is title</title>
<script>
var myVar = "global value"; // Declare a global variable
</script>
<script>
console.log("from another script:"+myVar);
</script>
</header>
<body>
Hello world
</body>
</html>
Of course, a harcoded url in the source code is not an option but understand this is the entry point to the next approaches. Almost all of them are based on this approach or do something similar.
#2 Webpack
Webpack provide us with several mechanisms like DefinePlugin
new webpack.DefinePlugin({
API_BASE_URL: JSON.stringify(process.env.API_BASE_URL)
})
The DefinePlugin is a direct text replacement. Webpack will look for the identifier and replace it with the given string. The following example illustrates how that works.
You could use this variable as if it were a global variable :
$.ajax({
type: "GET",
url: API_BASE_URL+'/odds?date=2019-01-19',
dataType: 'json',
success: function (data) {
...
}
});
}
Here more webpack mechanisms : https://stackoverflow.com/a/40416826/3957754
Advantages:
- Simple way to set or define several variables and use them throughout the application.
- Using a C.I server like jenkins you could set all your configurations and build your artifact an then deploy it:
export API_BASE_URL=http://awesome.api.com
export ENDPOINT_DETAIL=/detail
export ENDPOINT_FAVOURITE=/favourite
export MODULES=user,guest,admin,configure
npm run build
Disadvantages
- Variables are injected at build stage. Changes in your configurations will require a new build and redeploy of your application.
#3 Properties from SCM, Database or File System
Check this answer:
- What is the best way to change application configurations in a CI environment
#4 Centralized and manageable configuration
The main idea is put all your configurations, settings or properties in one site and all your applications must retrieve this values in a secure way. Nowadays the most common technique to retrieve this configuration from final application is perform an http request to an api rest published by the platform.
This kind of platforms are the perfect partners for a microservice architecture. Also I was able to use it for microfrontends.
Here some platforms:
- zookeeper
- http://www.therore.net/java/2015/05/03/distributed-configuration-with-zookeeper-curator-and-spring-cloud-config.html
- Spring Cloud
- https://www.baeldung.com/spring-cloud-configuration
- This is a java spring framework functionality in which you can create properties file with configurations and configure your applications to read them.
- magi-properties-management
- This is a java web system in which you can create environments and any key:value in each one. You just need configure your application in any language to read this values.
- cyber-properties-management
- This is a nodejs application that allows you to store properties files (.properties .yml or .json) and then just consume them as rest endpoint from your applications.
You can use one of these platforms in conjunction with webpack approach. So instead to manually environment variables export you can consume the api rest at build stage in webpack or with your C.I server like jenkins.
Advantages
- Everything commented above.
Disadvantages
- Unique properties file or manual environment exports are easy and fast to configure versus doing it on another server different to the application.
#5 /settings in your web
Asumming that your web is served at http://myapp.com, you could publish another endpoint like http://myapp.com/settings. This endpoint will return all the settings required by your microfrontend or web application at CLIENT SIDE using AJAX.
In your javascript application, in the entry point (commonly App.js in react, vue, etc), I mean before render mechanisms, you can consume http://myapp.com/settings and expose as global vars to your application. You can also save it in one of the storages available like, localStorage, sessionStorage, etc
Advantages
- Changes in your configurations are ready to use without rebuild the application. Just perform a page refresh to execute again the entry point in your javascript.
- You can use the approach #3 in the backend controller of your /settings
Disadvantages
- Pre hardcoded variables are instantly loaded versus an ajax http request.