<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged dotenv</title>
<link>https://coderlegion.com/tag/dotenv</link>
<description>Powered by Question2Answer</description>
<item>
<title>The Hidden Truth About Booleans in .env Files: Why 'false' Is Still Truthy in JavaScript</title>
<link>https://coderlegion.com/2965/the-hidden-truth-about-booleans-in-env-files-why-false-is-still-truthy-in-javascript</link>
<description>&lt;h1&gt;Understanding Environment Variables in Node.js&lt;/h1&gt;
&lt;p&gt;In Node.js (and frameworks like NestJS), environment variables, typically managed through &lt;code&gt;.env&lt;/code&gt; files and accessed using &lt;code&gt;process.env&lt;/code&gt;, &lt;strong&gt;are always strings&lt;/strong&gt;. This subtle fact can introduce &lt;strong&gt;unexpected bugs&lt;/strong&gt;, especially when you're expecting a boolean (&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;) but get a string (&lt;code&gt;&quot;true&quot;&lt;/code&gt; or &lt;code&gt;&quot;false&quot;&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;Real-World Example in a Node.js/NestJS Project&lt;/h2&gt;
&lt;p&gt;Suppose you have a .env file like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DB_SYNCHRONIZE=false
DB_LOGGING=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in your configuration code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;database: {
  synchronize: process.env.DB_SYNCHRONIZE,
  logging: process.env.DB_LOGGING,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You expect synchronize to be false and logging to be true.&lt;br&gt;
But in reality, both will be truthy because they are non-empty strings!&lt;/p&gt;
&lt;p&gt;The Bug in Action&lt;br&gt;
Why is this a problem?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;console.log(process.env.DB_SYNCHRONIZE); // &quot;false&quot; (string)
if (process.env.DB_SYNCHRONIZE) {
  console.log(&quot;This runs!&quot;); // This will run, even though you set it to &quot;false&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is because in JavaScript:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;false&quot; (string) is truthy&lt;/li&gt;
&lt;li&gt;Only &quot;&quot;, 0, null, undefined, and false (boolean) are falsy&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;Boolean(&quot;false&quot;); // true
Boolean(&quot;true&quot;);  // true
Boolean(&quot;&quot;)       // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, if you pass process.env.DB_SYNCHRONIZE directly to a library expecting a boolean, it will act as true!&lt;/p&gt;
&lt;p&gt;So &lt;code&gt;&quot;false&quot;&lt;/code&gt; (a non-empty string) is &lt;strong&gt;truthy&lt;/strong&gt;, and the &lt;code&gt;if&lt;/code&gt; block runs,  even though logically you set it to false!&lt;/p&gt;
&lt;p&gt;This behavior can lead to critical misconfigurations in production environments. For example, enabling logging, enabling DB synchronization (which drops and recreates tables in some ORMs), or accidentally exposing debug features.&lt;/p&gt;
&lt;p&gt;Always convert environment variable strings to booleans explicitly.&lt;/p&gt;
&lt;h2&gt;The Correct Approach: Explicit Type Conversion&lt;/h2&gt;
&lt;h2&gt;Boolean Conversion Pattern&lt;/h2&gt;
&lt;p&gt;Always parse environment variables to their expected types before use.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const synchronize = process.env.DB_SYNCHRONIZE === 'true';
const logging = process.env.DB_LOGGING === 'true';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;'true' === 'true'&lt;/code&gt; → &lt;code&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'false' === 'true'&lt;/code&gt; → &lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You now get predictable, safe boolean values.&lt;/p&gt;
&lt;h2&gt;Optional Utility Function&lt;/h2&gt;
&lt;p&gt;For larger projects, it's wise to create a helper:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function toBool(value: string | undefined): boolean {
  return value?.toLowerCase() === 'true';
}

const synchronize = toBool(process.env.DB_SYNCHRONIZE);
const logging = toBool(process.env.DB_LOGGING);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pro Tip: Use a Config Validation Library&lt;/h2&gt;
&lt;p&gt;Frameworks like NestJS recommend using a validation layer (e.g., with &lt;code&gt;Joi&lt;/code&gt; or &lt;code&gt;zod&lt;/code&gt;) to validate and transform config values at startup.&lt;/p&gt;
&lt;p&gt;Example using &lt;code&gt;Joi&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const configSchema = Joi.object({
  DB_SYNCHRONIZE: Joi.boolean().default(false),
  DB_LOGGING: Joi.boolean().default(false),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Real-Life Pitfall: When &quot;false&quot; Isn’t Really False&lt;/h2&gt;
&lt;p&gt;This isn’t just theory, I ran into this issue myself while working on a NestJS project using TypeORM.&lt;/p&gt;
&lt;p&gt;I had this in my .env:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;DB_SYNCHRONIZE=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And my config looked like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;synchronize: process.env.DB_SYNCHRONIZE,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I assumed this would disable TypeORM’s automatic schema synchronization, since I planned to use migrations instead.&lt;/p&gt;
&lt;p&gt;But when I started the app, the tables were still being created. Then, when I tried to generate a migration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;typeorm migration:generate -n AddNewFields
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I kept seeing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;No changes in database schema were found - cannot generate a migration.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It was confusing, until I logged the value of process.env.DB_SYNCHRONIZE and saw:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;console.log(typeof process.env.DB_SYNCHRONIZE); // string
console.log(process.env.DB_SYNCHRONIZE);        // &quot;false&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That string &quot;false&quot; is truthy, so synchronize: &quot;false&quot; is effectively treated as true in JavaScript. TypeORM was happily syncing the schema on every run, and by the time I tried to run the migration generator, there were no changes left to detect.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Misinterpreting environment variables is one of those bugs that can sneak into production unnoticed until something critical misbehaves. In systems where flags control behavior like syncing databases, enabling debug logs, or toggling features, treating &lt;code&gt;&quot;false&quot;&lt;/code&gt; as a boolean without conversion can be dangerous.&lt;/p&gt;
&lt;p&gt;Always treat &lt;code&gt;process.env&lt;/code&gt; values as &lt;strong&gt;untrusted strings&lt;/strong&gt; and explicitly cast them to the types your logic expects. Doing this upfront helps you avoid fragile assumptions and keeps your config layer predictable.&lt;/p&gt;
</description>
<category>Best Practices</category>
<guid isPermaLink="true">https://coderlegion.com/2965/the-hidden-truth-about-booleans-in-env-files-why-false-is-still-truthy-in-javascript</guid>
<pubDate>Sun, 11 May 2025 15:49:03 +0000</pubDate>
</item>
</channel>
</rss>