Exposing Environment Variables to Static Spine.js Apps
If you're using Spine.app to build your static Spine.js app, you might run into the same problem that we did: there was some configuration (such as the host of our API) that needed to change from environment to environment. I wanted to retain the completely static compilation process that Spine affords us without doing anything terribly hacky like client-side host-based conditionals.
Luckily, Spine.js apps are compiled using
hem and hem has a (not so well documented)
way to write custom compilers! I wanted to be able to selectively expose
environment variables (which are very easy to set on Heroku) and also supply
sensible defaults. So I decided that I would create a .env
compiler that
would parse a JSON hash as default values and override it with any available
environment variables.
All you have to do is put this into slug.js
in your project's root:
var hem = new (require(hem));
var fs = require(fs);
var argv = process.argv.slice(2);
hem.compilers.env = function(path) {
var content = fs.readFileSync(path, utf8);
var envHash = JSON.parse(content);
for (key in envHash) {
if (process.env[key]) {
envHash[key] = process.env[key];
}
}
return "module.exports = " + JSON.stringify(envHash);
};
hem.exec(argv[0]);
This simple script declares a compiler, then reads and parses a JSON file from
disk, replacing any declared keys with any environment variables that are
present. Now I can define a file (say app/environment.env
) that looks like
this:
{"API_HOST":"https://api.divshot.com","RUNTIME":"web"}
And I'm able to access and/or override those variables per-environment. For instance, in local development I can run:
API_HOST=http://localhost:8080 hem server
Or even better, if I'm compiling my app for offline use as a packaged app, I can pass a flag:
RUNTIME=chrome hem compile
All that is required to access these variables is to require the environment
file like you would any other:
window.env = require("environment")
console.log(env.API_HOST)
This is a simple hack that provides some powerful configurability. If you're using Spine.app, it might just come in handy!
Bonus: Making it work on Heroku
When I did this I did it with the target of deploying to Heroku using our Spine Heroku buildpack. When I deployed to Heroku...it didn't work! That's because by default Heroku doesn't expose config variables to the build process.
Luckily, there is a Heroku labs feature to expose config variables during the build process. Just run:
heroku labs:add user-env-compile
Now when the buildpack compiles your assets, it will detect and assign environment variables appropriately. We think this is a fantastic way to deploy static HTML with not-quite-static results.