Created
December 30, 2019 08:47
-
-
Save Sarfarazsajjad/946a02718b6385593693227a60b6e001 to your computer and use it in GitHub Desktop.
How to configure your AngularJS application using environment variables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<section class="posts wrapper"> | |
<article class="post post featured"> | |
<header> | |
<div class="feature"><span>Featured</span><i class="fa fa-bookmark fa-lg"></i></div> | |
<h2></h2> | |
<h1 id="posttitle"><a href="#">How to configure your AngularJS application using environment variables</a></h1> | |
<span> | |
Posted by <a href="https://twitter.com/jvandemo" target="_blank">Jurgen Van de Moere</a> | |
on <i class="fa fa-clock-o"></i> <time datetime="2016-02-24">February 24th, 2016</time>. | |
</span> | |
</header> | |
<section class="postbody"> | |
<p><img src="/content/images/2014/Aug/angular-bg-1.png" alt="featureimage" /></p> | |
<p>Most AngularJS applications contain logic.</p> | |
<p>And thus most AngularJS applications typically have a config, containing things like:</p> | |
<ul> | |
<li>the url of the API to communicate with</li> | |
<li>the base url of the application</li> | |
<li>whether or not to output debug logging</li> | |
<li>etc.</li> | |
</ul> | |
<p>Until recently I used to store such a config in an AngularJS constant:</p> | |
<pre><code class="language-javascript">var ngModule = angular.module('app', []); | |
// Example configuration stored as constant | |
ngModule.constant('config', { | |
apiUrl: 'https://your-api.com', | |
baseUrl: '/', | |
enableDebug: true | |
}); | |
</code></pre> | |
<p>so I could easily pass it around in my application using dependency injection:</p> | |
<pre><code class="language-javascript">ngModule.service('api', ApiService); | |
// Inject config so the api service does | |
// not have to be aware of the actual API url. | |
function ApiService(config){ | |
this.getUsers = function getUsers(){ | |
return $http | |
.get(config.apiUrl + '/users.json') | |
.then(function(response){ | |
return response.data; | |
}); | |
}; | |
} | |
// Inject dependencies | |
ApiService.$inject = ['config']; | |
</code></pre> | |
<p>but this turned out to be a painful mistake.</p> | |
<blockquote> | |
<p>This article is written for AngularJS 1.x applications. If you are looking for an updated article on Angular 2+, make sure to check out <a href="https://jvandemo.com/how-to-use-environment-variables-to-configure-your-angular-application-without-a-rebuild/">How to use environment variables to configure your Angular application without a rebuild</a>.</p> | |
</blockquote> | |
<h2 id="whythisapproachshouldbeavoided">Why this approach should be avoided</h2> | |
<p>The crucial downside of this approach is that <strong>the config is part of the application code</strong>.</p> | |
<blockquote> | |
<p>The application's config is considered everything that is likely to vary between deploys (staging, production, etc).</p> | |
</blockquote> | |
<p>This introduces multiple drawbacks:</p> | |
<ul> | |
<li><strong>Deploying an application in different environments requires different "builds" of the code.</strong> Imagine that your application needs to connect to a different API when running in a staging environment. You now have to create a different build of your application code.</li> | |
<li><strong>Code from private applications can not be made public.</strong> Not all applications are deployed publicly on the internet. Imagine that you hire an external consultant to work on your private application code. The consultant now has access to all private configuration data that is stored in your config.</li> | |
<li><strong>Not scalable</strong>. Modern cloud-based hosting infrastructure can scale dynamically. A fixed config prevents deployments tools from dynamically scaling and configuring your application as needed.</li> | |
</ul> | |
<p>These drawbacks painfully confirm the <a href="http://12factor.net/config">The Twelve-Factor App config rule</a> that states that there should always be a <strong>strict separation of config from code</strong>.</p> | |
<h2 id="howbackendengineerssolvethis">How back-end engineers solve this</h2> | |
<p>Back-end engineers have been facing the same problem for years.</p> | |
<p>They typically solve it by storing configuration details in environment variables. Then they read these environment variables from within their back-end applications. Problem solved!</p> | |
<p>Unfortunately front-end applications do not have access to these back-end environment variables.</p> | |
<p>So are we, front-end developers, doomed forever?</p> | |
<p>Fortunately not!</p> | |
<h2 id="thefrontendway">The front-end way</h2> | |
<p>Before we have a look at actual code, let's quickly recap what we are trying to do.</p> | |
<h3 id="whyarewedoingthis">Why are we doing this?</h3> | |
<ul> | |
<li>We want to be able to deploy our AngularJS application in different environments (staging, production, etc.) with different configurations <strong>without changing the AngularJS application code</strong>.</li> | |
<li>We want to be able to share our AngularJS application code with external parties at any given moment <strong>without leaking any confidential configuration details</strong>.</li> | |
</ul> | |
<h3 id="whatshouldwedotoaccomplishthis">What should we do to accomplish this?</h3> | |
<ul> | |
<li>We need to extract all configuration details out of our AngularJS application</li> | |
</ul> | |
<p>The only remaining question is how do we do that?</p> | |
<p>Time for code!</p> | |
<h2 id="step1simulatinganenvironment">STEP 1: Simulating an environment</h2> | |
<p>We already learned that back-end engineers use environment variables, so let's learn from their prior art and tackle the problem in a similar way.</p> | |
<p>Let's add a <code><script></code> element for a new file <code>env.js</code> to our <code>index.html</code> and make it load <strong>before</strong> our AngularJS application code:</p> | |
<pre><code class="language-xml"><html ng-app="app"> | |
<head> | |
<!-- Load environment variables --> | |
<script src="env.js"></script> | |
<!-- Load AngularJS application --> | |
<script src="app.js"></script> | |
</head> | |
<body> | |
... | |
</body> | |
</html> | |
</code></pre> | |
<p>The <code>env.js</code> file contains all our environment variables and looks like this:</p> | |
<pre><code class="language-javascript">(function (window) { | |
window.__env = window.__env || {}; | |
// API url | |
window.__env.apiUrl = 'http://dev.your-api.com'; | |
// Base url | |
window.__env.baseUrl = '/'; | |
// Whether or not to enable debug mode | |
// Setting this to false will disable console output | |
window.__env.enableDebug = true; | |
}(this)); | |
</code></pre> | |
<p>This will make a special (global) variable <code>__env</code> available in our browser window containing all configuration details for our application.</p> | |
<blockquote> | |
<p>You can open the console in your browser and type <code>window.__env</code> to display it.</p> | |
</blockquote> | |
<h2 id="step2loadingtheenvironmentinangularjs">STEP 2: Loading the environment in AngularJS</h2> | |
<p>Next we import the environment variables in our AngularJS app and make it available as a constant called <code>__env</code>:</p> | |
<pre><code class="language-javascript">// app.js | |
var env = {}; | |
// Import variables if present (from env.js) | |
if(window){ | |
Object.assign(env, window.__env); | |
} | |
// Define AngularJS application | |
var ngModule = angular.module('app', []); | |
// Register environment in AngularJS as constant | |
ngModule.constant('__env', env); | |
</code></pre> | |
<p>Now we can use AngularJS dependency injection to inject the environment variables in a config block to configure logging:</p> | |
<pre><code class="language-javascript">// app.js | |
function disableLogging($logProvider, __env){ | |
$logProvider.debugEnabled(__env.enableDebug); | |
} | |
// Inject dependencies | |
disableLogging.$inject = ['$logProvider', '__env']; | |
ngModule.config(disableLogging); | |
</code></pre> | |
<p>and in a service constructor function to configure our API service: </p> | |
<pre><code class="language-javascript">// app.js | |
ngModule.service('api', ApiService); | |
// Inject __env so we can access environment | |
// variables | |
function ApiService(__env){ | |
this.getUsers = function getUsers(){ | |
return $http | |
.get(__env.apiUrl + '/users.json') | |
.then(function(response){ | |
return response.data; | |
}); | |
}; | |
} | |
// Inject dependencies | |
ApiService.$inject = ['__env']; | |
</code></pre> | |
<p>Thanks to AngularJS dependency injection we can now access the environment variables anywhere in our AngularJS application.</p> | |
<p>The difference with the initial version using the <code>config</code> constant is that the actual values in the <code>__env</code> constant are now imported from an external script and are no longer part of the AngularJS application code.</p> | |
<h2 id="butwaitdoesntthisjustshifttheproblemtoenvjs">But wait, doesn't this just shift the problem to env.js?</h2> | |
<p>No, the default <code>env.js</code> in your code repository can contain no values at all or the configuration values that are needed to develop the application locally (and that can safely be shared with any other external developer):</p> | |
<pre><code class="language-javascript">(function (window) { | |
window.__env = window.__env || {}; | |
window.__env.apiUrl = 'http://localhost:8080'; | |
window.__env.baseUrl = '/'; | |
window.__env.enableDebug = true; | |
}(this)); | |
</code></pre> | |
<p>The deployment team can then overwrite the <code>env.js</code> file during deployment with values that should be used for that specific deployment such as staging or production:</p> | |
<pre><code class="language-javascript">(function (window) { | |
window.__env = window.__env || {}; | |
window.__env.apiUrl = 'http://production.your-api.com'; | |
window.__env.baseUrl = '/'; | |
window.__env.enableDebug = false; | |
}(this)); | |
</code></pre> | |
<p>Configuring the application using <code>env.js</code> can now be done by the deployment team and does not require a rebuild of the AngularJS application.</p> | |
<h2 id="summary">Summary</h2> | |
<p>By strictly separating all configuration details in <code>env.js</code>, our application can now be:</p> | |
<ul> | |
<li>deployed to different environments (staging, production, etc.) with different configurations <strong>without changing the AngularJS application code</strong></li> | |
<li>shared with external parties at any given moment <strong>without leaking any confidential configuration details</strong></li> | |
</ul> | |
<p>Which is exactly what we needed to accomplish.</p> | |
<p>You can find a <a href="http://plnkr.co/edit/XvFh4CkCYM7QgS9Fg8Kz">working example right here</a>.</p> | |
<p>Have a great one!</p> | |
<blockquote> | |
<p>This article is written for AngularJS 1.x applications. If you are looking for an updated article on Angular 2+, make sure to check out <a href="https://jvandemo.com/how-to-use-environment-variables-to-configure-your-angular-application-without-a-rebuild/">How to use environment variables to configure your Angular application without a rebuild</a>.</p> | |
</blockquote> | |
<h4 id="relatedlinks">Related links</h4> | |
<p>2017-04-28 — In <a href="https://www.intertech.com/Blog/deploying-angular-4-apps-with-environment-specific-info/">this article</a> Rich Franzmeier provides a way to accomplish a similar effect in Angular 4. <br /> | |
2018-08-13 — If you are looking for an updated article on Angular 2+, make sure to check out <a href="https://jvandemo.com/how-to-use-environment-variables-to-configure-your-angular-application-without-a-rebuild/">How to use environment variables to configure your Angular application without a rebuild</a>.</p> | |
</section> | |
<footer> | |
<ul class="share left"> | |
<li><a href="https://twitter.com/share?text=How%20to%20configure%20your%20AngularJS%20application%20using%20environment%20variables&url=http://www.jvandemo.com/how-to-configure-your-angularjs-application-using-environment-variables/" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;" class="smallbutton lightgray"><i class="fa fa-twitter"></i>Twitter</a></li> | |
<li><a href="https://www.facebook.com/sharer/sharer.php?u=http://www.jvandemo.com/how-to-configure-your-angularjs-application-using-environment-variables/" onclick="window.open(this.href, 'facebook-share','width=580,height=296');return false;" class="smallbutton lightgray"><i class="fa fa-facebook"></i>Facebook</a></li> | |
</ul> | |
</footer> | |
<div class="subscribe"> | |
<h2>Never miss an article again!</h2> | |
<p>Have all awesomeness delivered directly to your inbox:</p> | |
<div id="mc_embed_signup"> | |
<form action="//jvandemo.us7.list-manage.com/subscribe/post?u=f5d2e64a1e160cafcc91f74a8&id=3338f2c73d" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate> | |
<input type="email" value="" name="EMAIL" id="mce-EMAIL" placeholder="Your email address" required> | |
<div style="position: absolute; left: -5000px;"><input type="text" name="b_f5d2e64a1e160cafcc91f74a8_3338f2c73d" tabindex="-1" value=""></div> | |
<input type="submit" value="Sign up" name="subscribe" id="mc-embedded-subscribe" class="smallbutton blue"> | |
</form> | |
</div> | |
</div> | |
<div class="postprofile"> | |
<img src="/content/images/2015/Mar/jurgen.jpg" class="author" alt="user"> | |
<div class="info"> | |
<h4>Jurgen Van de Moere</h4> | |
<p>Front-end architect at The Force. Gymnast. Dad. Family man. Creator of <a href="http://www.angular-express.com/" target="_blank">Angular Express</a>.</p> | |
<a href="https://twitter.com/jvandemo" class="twitter-follow-button" data-show-count="false" data-size="large">Follow @jvandemo</a> | |
</div> | |
</div> | |
<div class="comments"> | |
<a href="javascript:;" class="readmore smallbutton blue"><i class="fa fa-comments"></i>View Comments...</a> | |
<div id="disqus_thread"></div> | |
<div id="g-comments"></div> | |
</div> | |
</article> | |
</section> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment