Skip to content

EnvVars

type: class
since: v1.0.0

The EnvVars class is a versatile utility designed for type-safe access to environment variables. It simplifies handling environment variables by offering features such as type parsing, retrieving values tailored to specific environments, reading from various sources, and marking variables as dynamic. Additionally, it ensures robust error handling, preventing your application from proceeding with unexpected behavior when issues arise.

The envVarsCustomEventEmitterErrorScope app error scope array is exported by this utility and can be used to filter thrown AppError by scope for this specific utility.
To learn more about error handling please refer to error handling section of the getting started guide, and the AppError documentation.

Imagine the following scenario:

let PORT = +(process.env.PORT || 3000);
let HOST = process.env.HOST || 'localhost';
if (process.env.NODE_ENV === 'production') {
PORT = +process.env.PRODUCTION_PORT || throw new Error('Missing production port');
HOST = process.env.PRODUCTION_HOST || throw new Error('Missing production host');
}

This code snippet can become cumbersome to write, maintain, and debug, especially when you have multiple environments and the same variable is used in different places throughout your app. The EnvVars class makes this code much easier to read and maintain, while also providing additional features and robust error handling.

Let’s take a look at how it works:

  1. Imagine your process.env object looks like this:

    {
    PORT: 3000,
    PRODUCTION_PORT: 5000,
    }
  2. Create an envVars.ts file:

    export const envVars = new EnvVars({
    mapObj: {
    port: {
    parseAs: 'number',
    whenNodeEnvIs: {
    production: 'PRODUCTION_PORT',
    anyEnv: 'PORT',
    }
    },
    }
    });
  3. EnvVars takes care of everything by selecting the appropriate value for the current environment, converting it to the correct type, marking it dynamic if specified, and throwing any errors to make sure that your app doesn’t run with unexpected behavior.
    Then you can use the previous envVars.ts file in your code as follows:

    import { envVars } from './envVars';
    console.log(envVars.port); // process.env.NODE_ENV === 'production' ? 5000 : 3000
  4. if the currentEnv is production the previous example will log the number 5000 else it will log the number 3000

Full Example

.env
SECRET_VALUES=1,2,3,4
envVars.ts
import { EnvVars } from '@mustib/utils/node';
export const fromObject = {
PORT: '3000',
}
export const envVars = new EnvVars({
useDynamicValues: true,
enumerable: true,
sources: {
fromFile: 'path to .env file',
fromObject,
fromDynamicFunction: () => ({
HOST: 'localhost',
})
},
mapObj: {
port: {
parseAs: 'number',
whenNodeEnvIs: {
anyEnv: 'PORT',
}
},
host: {
whenNodeEnvIs: {
anyEnv: 'HOST',
}
},
secretValues: {
parseAs(data) {
return data.varValueForCurrentEnv.split(',')
},
whenNodeEnvIs: {
anyEnv: 'SECRET_VALUES',
}
},
varWithDefaultValue: {
whenNodeEnvIs: {
anyEnv: {
varName: 'VAR_NAME_WITH_DEFAULT_VALUE',
defaultValue: 'any_string'
},
}
},
}
})
app.ts
import { envVars, fromObject } from './envVars';
console.log(envVars) // { port: 3000, host: 'localhost', secretValues: [ '1', '2', '3', '4' ] }
fromObject.PORT = '5000'
console.log(envVars) // { port: 5000, host: 'localhost', secretValues: [ '1', '2', '3', '4' ] }
console.log(Object.keys(envVars)) // [ 'port', 'host', 'secretValues' ]

Constructor()

type EnvVars = new (options: ConstructorParams<EnvVarsMapObj>): EnvVars

Parameters

  1. options
    type options = ConstructorParams<EnvVarsMapObj>;
    • useDynamicValues
      • A boolean indicating whether generated values should be dynamic.
      • default:
        false
    • sources
      • Defines source(s) of environment variables.
      • Can be a `.env` file path string, one EnvVarsSourcesObj, or an array of EnvVarsSourcesObj.
    • mapObj
    • currentEnv
      • A string representing the current environment.
      • default:
        process.env.NODE_ENV
    • enumerable
      • A boolean indicating whether generated keys should be enumerable.
      • default:
        false

EnvVarsMapObj

type EnvVarsMapObj = {
[varName: string]: {
parseAs?: ParseAsString | ParseAsFunction
useDynamicValue?: boolean
whenNodeEnvIs: {
[Env in (string & {}) | 'development' | 'production' | 'anyEnv']?:
| string
| {
defaultValue: string;
varName: string;
};
};
}
}

An object with variable names as keys and their configuration as object with the following properties:

  • parseAs: used to convert the value of the env-var from string to the specified type, possible values are:

    1. A string with the value of "string", "number", "bool", "date" where each value corresponds to a predefined parseAsFunction.
    2. A user-defined function whose return value determines the value and type of the environment variable.
  • whenNodeEnvIs: an object defining the environment variables that should be used for each environment.

  • useDynamicValue: a boolean indicates if the value is dynamic and will be parsed again every time it is needed

EnvVarsSourcesObj

type EnvVarsSourcesObj = {
fromFile: string;
fromObject: Record<string, string>;
fromDynamicFunction(): Record<string, string>;
};

An object that represents a source of environment variables with the following properties:

  • fromFile: A string representing the path to a .env file.
  • fromObject: An object with environment variables.
  • fromDynamicFunction: A function that returns an object with environment variables.

parseAsFunction

type ParseAsFunction = (data: {
combinedEnvVars: Record<string, string>[];
varValueForCurrentEnv: string;
currentEnv: string;
assignedSource: Record<string, string>;
varNameForCurrentEnv: string;
}) => any

A user-defined function whose return value determines the value and type of the environment variable.

It will be called with a data object with the following properties:

  • combinedEnvVars: an array of objects, each representing a source defined in the EnvVars constructor’s sources.

  • varValueForCurrentEnv: the value of the varNameForCurrentEnv in assignedSource object.

  • currentEnv: the current environment (the keys of the whenNodeEnvIs object).

  • assignedSource: the first object from combinedEnvVars that has the varNameForCurrentEnv

  • varNameForCurrentEnv: the name of the environment variable for the current environment (the value of the currentEnv from the whenNodeEnvIs object).
    For example:
    if whenNodeEnvIs has this value:

    const whenNodeEnvIs = {
    production: 'Prod_VAR',
    anyEnv: 'ANY_VAR',
    };

    then the varNameForCurrentEnv will be Prod_VAR for production and ANY_VAR for anyEnv

whenNodeEnvIs

type whenNodeEnvIs: {
[Env in (string & {}) | 'development' | 'production' | 'anyEnv']?:
| string
| {
defaultValue: string;
varName: string;
};
};

An object mapping environment names (like development, production, or any custom string) and/or anyEnv (as a fallback) to either:

  • A string representing the variable name in the combined sources

  • Or an object: since: v2.11.0

    • varName: the variable name in the combined sources
    • defaultValue: the default value to use if not specified in the combined sources

anyEnv acts as a fallback when the current environment does not match any other key.