12-factor for ember


#1

I’ve search, can’t find a reference to this and I guess it’s as emberjs is normally not deployed to a bunch of saas and on-prem sites with different configs for each.

I would like to know if there is a way to have a server-side configuration file that can be read and used as part of the environment or as variables in a service.

12-factor states that the ‘binary’ build is completely seperated from the configuration, this is what I’m trying to achieve.

https://12factor.net/config

The closest I’ve found is this: https://adrianperez.org/12-factoring-single-page-applications/ which recommends a pre-compiler just prior to installation.

Any alternatives you might be able to suggest are greatly appreciated.


#2

If I got you right, you like to apply the configuration after the build. This is achievable as part of deployment pipeline since ember-cli stores application configuration (config/environment.js) in meta tag of index.html.

Assuming you have a default config/environment.js for a new project created with ember-cli@3.1.4 the meta tag is as following for a production build:

<meta name="test-project/config/environment" content="%7B%22modulePrefix%22%3A%22test-project%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22/%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%7D%2C%22APP%22%3A%7B%22name%22%3A%22test-project%22%2C%22version%22%3A%220.0.0+ba125121%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D" />

The content is simply a JSON string encoded as URI component:

decodeURIComponent('%7B%22modulePrefix%22%3A%22test-project%22%2C%22environment%22%3A%22production%22%2C%22rootURL%22%3A%22/%22%2C%22locationType%22%3A%22auto%22%2C%22EmberENV%22%3A%7B%22FEATURES%22%3A%7B%7D%2C%22EXTEND_PROTOTYPES%22%3A%7B%22Date%22%3Afalse%7D%7D%2C%22APP%22%3A%7B%22name%22%3A%22test-project%22%2C%22version%22%3A%220.0.0+ba125121%22%7D%2C%22exportApplicationGlobal%22%3Afalse%7D');
// "{"modulePrefix":"test-project","environment":"production","rootURL":"/","locationType":"auto","EmberENV":{"FEATURES":{},"EXTEND_PROTOTYPES":{"Date":false}},"APP":{"name":"test-project","version":"0.0.0+ba125121"},"exportApplicationGlobal":false}"

#3

If this were me I would have a separate JSON document for each server. Have it in a known path (/public/serverconfig.json maybe),m have the index.html load the config, and then on app boot read the JSON form the DOM. There are other methods as well.

Here is an example instance-initializer that fetches the server config:

// File app/instance-initializers/server-config.js
const NULL_ELEMENT = { innerHTML: '' };

export function initialize(app) {
  let serverConfig = app.lookup('service:server-config');
  let configEl = document.getElementById('server-config') || NULL_ELEMENT;
  let json;
  try {
    json = JSON.parse(configEl.innerHTML);
  } catch (error) {
    console.warn('No server config available');
    return;
  }
  for (let key of Object.keys(json)) {
    serverConfig.set(key, json[key]);
  }
}

export default { initialize };

And in the head of your index.html:

<head>
  ...
  <script id="server-config" type="application/json" src="/serverconfig.json"></script>
</head>

We have an app that does something similar between different environments (prod, qa, develop) and part of the CI build scripts is to override a portion of the index.html with a script that adds server config to window. But I like the above better as it is a little more portable and can work in environments where the serverconfig.json is not available yet.


#4

Thanks very much for your replies. I think we need to embed with the ‘meta’ method as we deploy. My understanding is that the code runs in the browser so the example with .json being read in would only work on a local development PC and would not retrieve the json from the server.

You have both helped my understanding a lot. Thanks to both of you.


#5

The json is built by the individual server. The point is that the production app reads from the server that served it. Least that is how I understand the 12-factor intention. The example I gave has nothing conceptually different then the meta tag example. They would both do the same thing. The difference is in where you store the server config. In a meta tag or in a JSON document that is loaded by the browser. Both solutions do the same thing.


#6

You are right Sukima. I think this fixes my problem. I appreciate your help.


#7

The script tag has a src attribute but the initializer is reading the innerHTML of the script element. I think I am missing something. Why not make a request to the server and omit the script tag?