<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Roman</title>
    <description>The latest articles on DEV Community by Roman (@m0ltzart).</description>
    <link>https://dev.to/m0ltzart</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F209189%2Fe3564c96-d8e0-4413-be51-8d509762628c.jpeg</url>
      <title>DEV Community: Roman</title>
      <link>https://dev.to/m0ltzart</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/m0ltzart"/>
    <language>en</language>
    <item>
      <title>Configuring TypeScript Application the Third Factor Way</title>
      <dc:creator>Roman</dc:creator>
      <pubDate>Thu, 08 Aug 2019 19:27:40 +0000</pubDate>
      <link>https://dev.to/m0ltzart/configuring-typescript-application-the-third-factor-way-2ekp</link>
      <guid>https://dev.to/m0ltzart/configuring-typescript-application-the-third-factor-way-2ekp</guid>
      <description>&lt;p&gt;The &lt;a href="https://12factor.net/config" rel="noopener noreferrer"&gt;third factor&lt;/a&gt; of &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;The Twelve-Factor App&lt;/a&gt; document states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Config: Store config in the environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article explains how you can achieve this in TypeScript / NodeJS applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;Environment variables are available in each NodeJS process. NodeJS process inherits them from the process that launched the application.&lt;/p&gt;

&lt;p&gt;During development, these variables come from your shell session via terminal, and in production these variables come from the process supervisor (e.g. &lt;code&gt;systemd&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can access environment variables via built-in variable &lt;code&gt;process.env&lt;/code&gt; - it is a dictionary of key/value strings.&lt;/p&gt;

&lt;p&gt;You can see all of the variables that would be available to your process, in the current environment with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'console.log(process.env)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also supply environment variables for each command execution by prefixing the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt; node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'console.log(process.env.API_KEY)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cross-Platform
&lt;/h2&gt;

&lt;p&gt;Not every operating system (&lt;em&gt;Windows, I am looking at you!&lt;/em&gt;) supports setting command-line environment variables.&lt;/p&gt;

&lt;p&gt;Of course, the NodeJS community thought of that, and there is a module &lt;a href="https://www.npmjs.com/package/cross-env" rel="noopener noreferrer"&gt;&lt;code&gt;cross-env&lt;/code&gt;&lt;/a&gt; that solves this problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cross-env &lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'console.log(process.env.NODE_ENV)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problems With Environment Variable Values
&lt;/h2&gt;

&lt;p&gt;While you can access any variable directly via &lt;code&gt;process.env&lt;/code&gt;, it is not a good idea for a few reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is no required variable check. If your application reads from &lt;code&gt;process.env.API_KEY&lt;/code&gt; variable and you forget to set it in production - your application launches but fails when the code tries to access the non-existing variable.&lt;/li&gt;
&lt;li&gt;There is no data validation. Your system may expect a valid URL and get garbage instead.&lt;/li&gt;
&lt;li&gt;There is no type coercion. The values in &lt;code&gt;process.env&lt;/code&gt; are &lt;strong&gt;always strings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;There is no IntelliSense or code completion for &lt;code&gt;process.env&lt;/code&gt; keys, as the values are unknown until runtime.&lt;/li&gt;
&lt;li&gt;Easy to introduce a typo, e.g. &lt;code&gt;process.env.APIKEY&lt;/code&gt; instead of &lt;code&gt;process.env.API_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Difficult to unit test code directly relying on &lt;code&gt;process.env&lt;/code&gt;. It requires munging of the global &lt;code&gt;process.env&lt;/code&gt; , which can lead to side effects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is a typical mistake:&lt;/p&gt;

&lt;p&gt;Imagine you run your application with the environment variable set to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cross-env &lt;span class="nv"&gt;DESTROY_DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your code looks like this. Do you see a problem here?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY_DATABASE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that &lt;code&gt;process.env.DESTROY_DATABASE&lt;/code&gt; is &lt;strong&gt;always a string&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The variable is set to a literal string value of &lt;code&gt;"false"&lt;/code&gt;. It always evaluates to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another common mistake may involve numbers. Guess what the output is?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cross-env &lt;span class="nv"&gt;COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'console.log(process.env.COUNT + 1)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, it won't be &lt;code&gt;2&lt;/code&gt;, as that would be too easy, right? The correct answer is &lt;code&gt;11&lt;/code&gt;, because it is doing string concatenation, instead of math.&lt;/p&gt;

&lt;p&gt;The value of the environment variable is &lt;strong&gt;always a string&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One of the Solutions
&lt;/h2&gt;

&lt;p&gt;I am going to outline one way to solve these problems, but there is more than one way.&lt;/p&gt;

&lt;p&gt;I prefer to use the &lt;a href="https://www.npmjs.com/package/env-var" rel="noopener noreferrer"&gt;&lt;code&gt;env-var&lt;/code&gt;&lt;/a&gt; module to extract and validate the data.&lt;/p&gt;

&lt;p&gt;However, I also do it in a way that makes it easier to stub and test the configuration.&lt;/p&gt;

&lt;p&gt;I typically create a &lt;code&gt;config.ts&lt;/code&gt; file in the project root that exports a configuration class.&lt;/p&gt;

&lt;p&gt;First, here is a full example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;env-var&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;processEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processEnv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;HOME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HOME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;asString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;DESTROY_DATABASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DESTROY_DATABASE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;asBool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;COUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COUNT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;asIntPositive&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break it down.&lt;/p&gt;

&lt;p&gt;We declare a private property &lt;code&gt;processEnv&lt;/code&gt; which defaults to values from &lt;code&gt;process.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Have defaults allows us to construct the objects with defaults in your app code.&lt;/p&gt;

&lt;p&gt;You'll probably use dependency injection and provide an instance of the &lt;code&gt;Config&lt;/code&gt; automatically to your entire app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, for tests, we can always override the defaults and provide our values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we also freeze the object in the constructor, so that every instance becomes immutable.&lt;/p&gt;

&lt;p&gt;There would be no way for you to overwrite the config object in your code accidentally.&lt;/p&gt;

&lt;p&gt;The config should always be immutable.&lt;/p&gt;

&lt;p&gt;The next step is to use &lt;code&gt;from&lt;/code&gt; method provided by &lt;code&gt;env-var&lt;/code&gt; to instantiate the &lt;code&gt;env-var&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Then we can declare any number of properties on the object and using a builder pattern declare all of our requirements for the given variable. &lt;/p&gt;

&lt;p&gt;Refer to the &lt;a href="https://www.npmjs.com/package/env-var" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to learn about all of the possible validations. There are a lot of goodies there. You can set defaults and even decode from Base64.&lt;/p&gt;

&lt;p&gt;This solution is entirely type-safe. You can see the &lt;a href="https://github.com/moltar/typescript-12-factor" rel="noopener noreferrer"&gt;code and tests in my repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is a screenshot showing how VS Code IntelliSense picks up correctly that &lt;code&gt;DESTROY_DATABASE&lt;/code&gt; is a boolean.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmoltar%2Ftypescript-12-factor%2Fmaster%2Fintellisense.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmoltar%2Ftypescript-12-factor%2Fmaster%2Fintellisense.png" alt="VS Code IntelliSense"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Ideas
&lt;/h2&gt;

&lt;p&gt;Do you have any tips and tricks for managing configuration?&lt;/p&gt;

&lt;p&gt;Please share them in the comments below.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>twelvefactor</category>
      <category>configuration</category>
      <category>config</category>
    </item>
  </channel>
</rss>
