npm.io
1.1.0 • Published 14h ago

@mostajs/config

Licence
AGPL-3.0-or-later
Version
1.1.0
Deps
0
Size
26 kB
Vulns
0
Weekly
0

@mostajs/config

Author: Dr Hamid MADANI <drmdh@msn.com> License: AGPL-3.0-or-later

Environment variable loader with profile cascade for the MostaJS ecosystem. One .env file, multiple profiles (TEST, DEV, STAGING, PROD), switched via a single MOSTA_ENV variable.

Install

npm install @mostajs/config

Why

Instead of juggling .env.test, .env.development, .env.production and forgetting to sync them, keep one .env with profile-prefixed overrides.

# .env — commit the non-secret keys
MOSTA_ENV=TEST                   # active profile

DB_DIALECT=sqlite                # plain default (also the TEST default)
SGBD_URI=./data.sqlite

DEV_DB_DIALECT=postgres
DEV_SGBD_URI=postgres://localhost:5432/devdb

PROD_DB_DIALECT=mongodb
PROD_SGBD_URI=${MONGO_ATLAS_URI} # secrets come from the orchestrator

Resolution cascade (first non-empty wins) :

  1. ${MOSTA_ENV}_${key}
  2. ${key}
  3. fallback arg
  4. undefined

Usage

import { getEnv, getEnvBool, getEnvNumber, getCurrentProfile } from '@mostajs/config';

// String with fallback
const dialect = getEnv('DB_DIALECT', 'sqlite');

// Boolean (only 'true' — case-insensitive — returns true)
const showSql = getEnvBool('DB_SHOW_SQL', false);

// Number with fallback
const poolSize = getEnvNumber('DB_POOL_SIZE', 10);

// Diagnostic
console.log(`Running in profile: ${getCurrentProfile() ?? 'none'}`);

Loading a .env file (v1.1.0, zero-dependency)

getEnv* read process.env. To populate it from a .env file without dotenv, call loadEnv() once at startup:

import { loadEnv, getEnvNumber } from '@mostajs/config';

loadEnv();                                   // reads <cwd>/.env into process.env
loadEnv({ path: '/etc/app/.env', override: true });

const port = getEnvNumber('PORT', 3000);
  • Never throws if the file is missing — returns { loaded: false }.
  • By default does not overwrite variables already in the environment (the real env wins over the file); pass override: true to invert.
  • parseEnv(content) is exposed for pure parsing (no side effect): handles export prefixes, single/double quotes (with \n \t \r \" \\ escapes), and inline # comments on unquoted values (a # with no leading space is kept, so URLs / JSON / hashes are safe).

Silent fallback guarantee

The loader never throws on missing keys. If a profile override is absent, it silently falls back to the plain variable or to the fallback argument. Callers decide whether undefined is a fatal condition.

process.env.MOSTA_ENV = 'STAGING';
// Neither STAGING_REDIS_URL nor REDIS_URL defined
getEnv('REDIS_URL')                 // undefined (no throw)
getEnv('REDIS_URL', 'redis://...')  // 'redis://...' (fallback)

Empty strings are treated as unset

To avoid accidental blank values silently propagating, an empty string in a profile override falls through to the plain variable :

TEST_DB_DIALECT=
DB_DIALECT=sqlite
getEnv('DB_DIALECT')  // 'sqlite', not ''

Whitespace in MOSTA_ENV is trimmed

MOSTA_ENV=  TEST

Resolves to profile TEST. getCurrentProfile() returns 'TEST'.

Naming convention

  • Profile name → uppercase (TEST, DEV, PROD, STAGING)
  • Variable name → UPPER_SNAKE_CASE (DB_DIALECT, SGBD_URI, REDIS_URL)
  • Profiled key → {PROFILE}_{VARIABLE} — underscore separator (TEST_DB_DIALECT, not TEST.DB_DIALECT because the dot is rejected by POSIX shells)

Testing

npm run build
npm test

License

AGPL-3.0-or-later. See LICENSE.

Keywords