SPA - Server Side Injection into Static HTML

If you decide to generate all your client-side assets and ship them using a CDN, it can be tricky to inject server-side generated dynamic content.

Why would you do that?

There are some parameters that are available to the server and cannot be reliably retrieved through AJAX calls. HTTP headers such as referrer, user agent, client IP… and other fields that are transmitted by the browser upon requesting index.html, can be used to alter the behavior of your SPA before any of your libraries are loaded.

Examples include: environment variables, tracking codes, api codes, geo-location based redirecting, device based shipping of assets…

If this information is needed before page load, and needs to be synchronously retrieved (not through AJAX), you can use the following JS generation hack to provide it.

This example assumes you are using Express.JS in Node.JS. The same concept can be applied to any dynamic language that lets you manipulate HTTP response headers.

Your index.html

<script>
<pre>
<html>
<head>
<script src="env.js"></script>
</head>
<body>
<script src="jquery.js"></script>
<script src="bootstrap.js"></script>
</body>
</html>
</pre>
</script>

The generated env.js file

if (!(window.env && typeof window.env == "Object"))
{
    window.env = {};
}
window.env.API_KEY = 'XXXXXXX'; // API Key
window.env.GA_ID = 'YZYZYZYZ'; // Google Analytics ID
window.env.API_SERVER = 'lb1.apiserver.com'; // In case of zero-down time toggling

Normally this file would be hardcoded and served using the express static file server. This is fine until you need to make a change, requiring you to regenerate the code in the file and re-deploy it. A quick alternative is to generate the JS file on the fly and trick the browser into thinking its a static JS asset.

ExpressJS:

// JSON response
app.get('/data',  (req, res) => {
  res.send(getData());
})
// JS file
app.get('/data.js',  (req, res) => {
    res.type('application/javascript');    
    res.send(`window.GLOBAL = ${JSON.stringify(getData())}`);
})

The additional delay caused by this should be very minimal. If desired, one can trick the HTTP cache header using ETags to never fetch the file if it has not changed.

Demo

Static HTML

AJAX Fetch

Static JS

Server: Difference between server time and data ready
Took: Total time to render