motdotla / dotenv

Loads environment variables from .env for nodejs projects.

Home Page:https://www.dotenvx.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Boolean and null values

mikeshawdev opened this issue · comments

If there are environment variables set to null or false, they are cast as strings when saved to process.env

When they are read them into process.env there could be a check for boolean and null values, to ensure they are cast as such?

What do you reckon?

If you're performing boolean logic based on environment variables, you could just check for "true"

if (process.env.SHOULD_SEND_EMAIL == 'true') {
  mailer.send()
}

You could also use a zero (0) or one (1) instead of null or false to achieve boolean logic. You can just omit from your .env when you want that "false" case.

SHOULD_SEND_EMAIL=1
if (process.env.SHOULD_SEND_EMAIL) {
  mailer.send()
}

Aye, you could do that. However, I would argue that this is unexpected behaviour. If I set a value in my .env file to true/false/null I would expect them to be cast as such in the environment

I just ran into this same issue and I don't think you're suggestion for 1's and 0's will work. It seems that all env variables are coming over as strings, even numerical values, which makes your if condition always true.

Edit: After further research, it seems all environment variables are supposed to be strings. http://stackoverflow.com/a/10265271

@bstanley0811 thanks for the info.

Here the documentation for process.env which is indeed all strings.

I would say process.env shouldn't be used for flags. If you do need one tho I would just not set the varible or set the variable to an empty string which is a falsey value.

Thanks @jcblw, looks to be that process.env forces everything to be strings no matter what. I didn't realise this before

I guess if you wanted to get booleans/null out of it you could move them into your own object and cast them as needed

Cheers!

Hey guys I had this same issue so I built this plugin for dotenv at https://github.com/niftylettuce/dotenv-parse-variables. Hope it helps 👍

I did not expect .env to parse true and false as string values. I probably won't be the last person to make this assumption. Perhaps the Readme segment about parsing should be updated, or maybe a FAQ entry about booleans could clear things up.

Sorry to beat a dead horse:
Even though it is true process.env reads booleans as strings, its unexpected that the package we download (dotenv) to help us manage envs better would do this as well. Packages should make coding easier and as this thread clearly shows, most developers would assume that dotenv loads booleans into nodejs as, well, booleans. Its not intuitive that we should download a whole plugin for what should be expected behavior. This is a great package (kudos to the devs despite this mini-critique) and I believe this should be a feature enhancement.

It looks like the concept of "dotenv" originated with a Ruby library that was first released in 2012: https://github.com/bkeepers/dotenv. The google search trends for "dotenv" also support this: https://trends.google.com/trends/explore?date=all&geo=US&q=dotenv

This library, a nodejs implementation, was released in 2013. Due to javascript's growth, it does have more stars.

However, dotenv files are not javascript files. It seems like they are actually more closer to a Bash file than anything else?

I agree it would be useful to be able to have boolean values in a config file. However, it would also be nice if .env files were consistent. For example, in ruby the null value is spelled Nil, while of course in javascript it is null. Fortunately, true and false are spelled the same, but it could have easily been different. Python uses the spellings of True and False.

I don't have a clear answer here... I just wanted to draw some attention to this side of things, as I didn't see this aspect mentioned in any of the comments above.

Using env variables makes sense to maintain consistency between projects whether they're based on JavaScript or not. It follows that .env files themselves contain the same input as the shell variable.

That said, this does move type casting for numerical or boolean env values into your application logic. This can lead to duplication if you're using the same variable in multiple places.

After running into this issue, I wrote a new env loading library designed to handle type casting for both shell environmental variables and .env files: env-smart

I hope it's helpful to someone else who ends up here!

In my application, using Vue+Vuex, I'm using a store module to keep all of the app settings/config in one place (therefore removing myself from the process.env being all strings issue). I then parsed the dotenv like so:

var env = dotenv.parse('.env');
// Convert 'true' and 'false' to boolean, and numbers to numbers.
each(env, (value, key) => {
  if (value === 'true') env[key] = true;
  if (value === 'false') env[key] = false;
  if (value === 'null') env[key] = null;
  if (!isNaN(Number(value))) env[key] = Number(value);
});

Now my store is populated with the types that I'm expecting.

I was expecting true/false/0/1 values to be loaded as boolean/numeric types, this created a confusion for a while, I had to debug my project to find out what was wrong and I landed at this page afterwards.

The way I am handling true/false checks is by using empty (for false) and non empty (for true) strings.

false:
DEV_MODE=

true
DEV_MODE=anything

I like the suggestion from @centerorbit above for parsing into actual boolean values.

For anyone who landed here after a google looking for some way to do boolean-ish feature flags, here's one other idea (example in TS):

Some library helper:

import * as dotenv from 'dotenv';
dotenv.config();

/**
 * Gets an environment variable, or falls back to a default if not set.
 *
 * @param envVarName - the environment variable name
 * @param defaultValue - a default value to use if the environment variable is not set
 * @returns `process.env[envVarName]` or, if not set, the given `defaultValue`.
 */
export function resolveFromEnvOrDefault(envVarName: string, defaultValue: any) {
    return process.env[envVarName] ?? defaultValue;
}

Elsewhere in your codebase:

const someFeature = resolveFromEnvOrDefault('SOME_FEATURE_FLAG', 'OFF') === 'ON';
if (someFeature) { 
    console.log('feature is enabled');
}

.env file:

# comment out the line below (or change it to OFF, or really anything other than ON) to disable
SOME_FEATURE_FLAG=ON

The code is longer / less verbose but it might be OK if you want to make self-documenting .env files using words other than true/false.