Reputation: 958
I'm using react-boilerplate as the base for my project.
I'm currently defining my API url in webpack.base.babel.js like so:
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
PIZZA_API_URL: JSON.stringify('https://some-ip:8081')
},
}),
However, this is only picked up at build time.
In create-react-app, you can use REACT_APP_PIZZA_URL. Anything that starts with REACT_APP* can be set at the start of runtime.
So I can do:
docker run -e "REACT_APP_DB_HOST=HELLOWORLD" -d -p3000:3000 pizza-supplier-ui:test
How do you do the same thing with react-boilerplate?
Thank you
Upvotes: 0
Views: 963
Reputation: 53
You're right in saying that the environment variables your React app consumes are in fact injected at build time, and thus don't allow for truly dynamic configuration.
To solve this issue, you need to realise that in order to achieve what you want the contents of the static files served to your clients (which make up your React application) need to be changeable/changed at runtime.
You can achieve this in different ways, but it will always incur you having to manipulate some file or create a file (by manipulating it at runtime with your desired env var) and serve that after making those changes. There are 2 ways I can think of doing this:
The easy way is to leverage the order that <script/>
tags get loaded into the HTML file (check this StackOverflow answer for more insight on that). You want to import a JavaScript file, which will be served from your server. Your server will dynamically create this file at runtime, depending on the environment variable your server reads. So in the <head> ... </head>
section of your HTML, add a script tag such as: <script src=%PUBLIC_URL%/apiConfig.js
and reference this "transient" JS file.
Now, you need to configure a GET request mapping on your server so that when it receives a GET request for <base-url>/apiConfig.js
, it returns a JavaScript file. In a Spring Boot project, you can have a class such as:
@RestController
@CrossOrigin
public class ApiController {
String apiURL = System.getenv("apiUrl");
@GetMapping(value = "/apiConfig.js", produces = "application/javascript")
String getApiConfig() {
String body = "window._apiconfig_ = {API_URL: \"" + apiURL + "\"};";
return body;
}
}
Finally, in your React code, you need to reference the global window object, which is where your API_URL should exist. To reference that I'd use: window?._apiconfig_?.API_URL
This variable now has the environment variable which your Java controller read at runtime.
This method was strongly inspired by this online tutorial, check that out for a slightly different way to do this.
Also - this opens up a door for someone to literally inject code into your JavaScript, so it is a really bad idea in terms of security... If that is a concern at all, don't do this.
The second method would be to create a dummy string i.e. CHANGE_THIS_API_URL_AT_RUNTIME, and at runtime configure your server so that it reads an environment variable, scans your static files and replaces the dummy string with the environment variable (which is the API URL that you dynamically wanted to set).
Both solutions are really hacky, introduce security issues, and would degrade performance. In the first method I propose, there would be a blocking fetch to a JavaScript file, which is being generated on the server - you can get the server to cache this response, which would speed things up from the server side, but the user would still be doing that request.
Upvotes: 1
Reputation: 958
Ok - here is the answer to my own question after hours of research and trial & error.
For production build, webpack will compile your js files and bundle them up in build/ directory.
The API URLs that are referenced as ${process.env.API_URL}/api/query/findPizzaById/
are baked into the compiled js files in the build/ directory.
Therefore this command:
docker run -e "API_URL=HELLOWORLD" -d -p3000:3000 pizza-supplier-ui:test
has absolutely no effect. It does however register API_URL as an environment variable on the running docker image. The problem here is that API_URL is defined at BUILD TIME, using the environment variable FROM where the build has run.
If anyone has struggled with this issue and have a solution I'd appreciate your enlightenment!
Thanks
Upvotes: -1