How to configure a SPA on loading?
Asked Answered
H

5

5

We are using Webpack, React, Node.JS but I think this question is more generic that the specific technologies. I can use Webpack to configure the SPA when building for development mode or production mode (e.g. using the DefinePlugin).

How can I configure a SPA in production mode (configured at build) for different deployment environments (e.g. staging vs production)? For example, these different deployments would talk to different backend server APIs.

Somehow the SPA has to pickup some local context from the server as it is being GET'ed by the browser. Or perhaps we have to have a custom configuration file on each server that the SPA can securely GET?

We are using NodeJS on the server and this SPA will eventually be running as an isomorphic app so that could help. We are deploying these applications in Docker images and it's easy to configure their environment on deployment.

Thanks for any suggestions.

Heinrik answered 4/12, 2016 at 1:24 Comment(10)
combination between separate webpack configs and environment variables?Astragal
It's possible to build a number of different SPAs (using webpack configs and environment variables) but that's configuring at build time not "run" time.Heinrik
You can try browserify and dotenv node module?Deemster
Thank you for the suggestion but I am not sure how that would help configuring the SPA on the client at load/run-time. I can already do similar (loading config files) on the server based on an environment variable but not on the client.Heinrik
How do env variables not provide runtime options? Things like api endpoints especially paired with definePlugin now you have runtime variables. I'm not sure what else you could possibly need.Astragal
@Astragal the environment in the Web browser is not the same as the server. When the SPA runs in the Web browser it does not have access to the environment in the server. Anything we specify at build time for SPA then cannot change with deployment to different servers.Heinrik
Sure you can, that's exactly what defineplugin is for. your webpack config grabs the env variable then you use it in your client side codeAstragal
You should be able to build from your docker container, and if you can't do that then you'll need to setup an api endpoint to fetch server details for you. not sure what else you could wantAstragal
Building from docker container locks in current environment (not container deployment environment). Fetching details from an API is a possibility (a central discovery point) but I think serving a cookie with the SPA is probably the best (see my - not personally mine - solution below).Heinrik
@AshleyAitken all the right points, I am struggling as well. Couldn't find decent content on this as well. Also, only configuration I would like to get for SPA app is server endpoint (URL). Ideal solution is to somehow define endpoint in Dockerfile only and pass it as ENV variable in Docker Run command. Seems very difficult thought.Rondo
H
2

I found one way of doing what is required. It is by setting a cookie with configuration details when serving the SPA. The SPA can then read that cookie to get the configuration (and delete the cookie because it is not needed any more).

There is a NPM module called ClientConfig that will assist in doing what I have described. It works very similar to a companion NPM module called GetConfig that helps with configuration on the server side. ClientConfig: https://github.com/henrikjoreteg/clientconfig

How to use ClientConfig and GetConfig (separately and together) is described here: http://read.humanjavascript.com/ch12-settings-and-configs.html

This seems like a solution to me though I wonder about any potential security issues (that's alway more complex than first appears) and if there is not an easier approach. Any comments or further solution would be appreciated.

Heinrik answered 4/12, 2016 at 9:42 Comment(2)
Please explain why you think this is not a solution - I think it is the solution!Heinrik
Will this work if page will be loaded from browser cache?Lynettelynn
V
1

One way is to rewrite the contents of the HTML file upon deploy.

You can have a placeholder string, e.g. "$MY_CONFIG_HERE$" in your HTML.

This can be in an some inline javascript tag on the page, which will set a javascript object on window.

Then have your deploy process (Continuous Deploy) replace that string with the actual javascript object containing the data you want.

Then, the data will be available on window to your javascript running in the Single Page App.

Vanscoy answered 2/9, 2019 at 2:24 Comment(0)
I
0

Solution

Generate and load a separate script:

<html>
  <head>
    <script src="env.js"></script>
  </head>
  <body>
    <script src="main.js" async></script>
  </body>
</html>

At deploy time (or via a dynamic server), populate env.js:

process = window.process || {};
process.env = window.process.env || {};
process.env.API_URL = 'https://api.example.com';

(You don't need to use process.env, but it is commonly used, even in frontend code.)

Then reference in main.js:

console.log(process.env.API_URL);

This requires a blocking download (at least, if main.js is marked async), but it's only a single request. And you can add some (short) cache headers.

Alternatives

Modifying HTML

It is often a bit cleaner and easier to generate an entire JS file than modify an HTML one.

Furthermore, injecting inline scripts is problematic for CSP (Content Security Policy).

Set-Cookie

This is rife with problems with CDNs and caches and users clearing cookies.

Inquisitionist answered 14/5, 2023 at 4:16 Comment(0)
S
-1

We are struggling with the same concepts right now. The best way I found to configure at run time is by using env variables (which can be set at build time but also overriden at runtime using docker native or any other orchestration tool such as ECS or GKE).

Another, dirtier way, we used before is performing the run-time adjustments via the CMD directive of the image. This is not really recommended since it makes your image mutable and can make it prone to errors. However - it does work, and if used wisely can achieve what you want. A simple example for this is replacing your current CMD which probably looks a bit like this CMD node app.js with something like this - bash -c "wget prod.conf && node app.js"

Stop answered 4/12, 2016 at 8:10 Comment(1)
I am not sure how one can set environment variables for the Web browser. I was setting all the environment variables I could on the server but, of course, they couldn't affect the client. Similarly, for the CMD directive on the Docker image/container. The SPA has already been built within the Docker image.Heinrik
H
-1

Our current code uses WebPack DefinePlugin but I don't believe this allows one to do what we need. I have also found the ExtendedDefinePlugin which mentions the client but again, I am unsure if it is a possible solution:

https://github.com/ArikMaor/extended-define-webpack-plugin https://www.npmjs.com/package/extended-define-webpack-plugin

The DefinePlugin is also discussed here:

Passing environment-dependent variables in webpack

But again I don't believe this will allow us to configure the client SPA based upon the deployment context rather than the build context.

Heinrik answered 4/12, 2016 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.