UNPKG

good-env

Version:

Better environment variable handling for Twelve-Factor node apps

278 lines (201 loc) • 7.58 kB
# good-env <p align="center"> <img src="./img/good-env-logo.svg" alt="good-env-logo" width="400"/> </p> ![workflow](https://github.com/recursivefunk/good-env/actions/workflows/ci.yml/badge.svg) [![js-semistandard-style](https://raw.githubusercontent.com/standard/semistandard/master/badge.svg)](https://github.com/standard/semistandard) 🚨 v7 requires Node version 18.20.4 or higher! 🚨 # good-env A more intuitive way to work with environment variables in Node.js applications. [![npm version](https://img.shields.io/npm/v/good-env.svg)](https://www.npmjs.com/package/good-env) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) ## Why good-env? When building non-trivial applications, working with environment variables as raw strings can be limiting. `good-env` provides: - Type conversion (strings to numbers, booleans, lists, etc.) - Default values - Existence checking - Validation - No production dependencies ## Installation ```bash npm install good-env --save ``` ## Usage ### Basic Usage Import the package: ```javascript const env = require('good-env'); ``` ### Getting Values #### Simple Values ```javascript // Get a string (default behavior) env.get('HOST'); // 'localhost' // With a default value if not set env.get('NOT_SET', 'default'); // 'default' ``` #### Type Conversion ```javascript // Get as a number env.getNumber('PORT'); // 8080 (as a number, not string) env.num('PORT'); // Shorthand for getNumber() // Get as a boolean env.getBool('DEBUG'); // true (converts 'true' string to boolean) env.bool('DEBUG'); // Shorthand for getBool() // Get as a list env.getList('ALLOWED_ORIGINS'); // ['localhost', 'example.com'] env.list('ALLOWED_ORIGINS'); // Shorthand for getList() // Get a numeric list env.list('VALUES', { cast: 'number' }); // [1, 2, 3] (converts from '1,2,3') // Get a duration in milliseconds // CACHE_TTL=5m env.getDuration('CACHE_TTL'); // 300000 env.duration('CACHE_TTL'); // Shorthand for getDuration() // With a default — strings are parsed, numbers are treated as ms env.getDuration('CACHE_TTL', '30s'); // 30000 if CACHE_TTL is not set env.getDuration('CACHE_TTL', 1000); // 1000 if CACHE_TTL is not set // Supported units: ms, s, m, h, d, w (case-insensitive, decimals allowed) // Returns null if the value and default are both unparseable. // Get a Date // RELEASE_AT=2024-01-15T10:30:00Z env.getDate('RELEASE_AT'); // Date object env.date('RELEASE_AT'); // Shorthand for getDate() // Defaults may be ISO strings or Date instances env.getDate('RELEASE_AT', '2024-01-01'); // Parsed if RELEASE_AT is not set env.getDate('RELEASE_AT', new Date()); // Returned as-is if RELEASE_AT is not set // Follows JavaScript Date parsing rules — prefer ISO 8601 with a timezone. // Returns null if neither the value nor the default is a valid date. ``` #### URLs and IPs ```javascript // Get as a URL object // API_ENDPOINT=https://api.example.com/v1 const apiUrl = env.getUrl('API_ENDPOINT'); // Returns: // { // httpOk: true, // redisOk: false, // pgOk: false, // href: 'https://api.example.com/v1', // raw: URL { ... } // Native Node.js URL object // } env.url('API_ENDPOINT'); // Shorthand for getUrl() // Supported protocols: http, https, redis, postgresql // DATABASE_URL=postgresql://user:pass@localhost:5432/mydb const dbUrl = env.getUrl('DATABASE_URL'); // Returns: { pgOk: true, redisOk: false, httpOk: false, href: '...', raw: URL {...} } // Get an IP address (with validation) env.getIp('SERVER_IP', '127.0.0.1'); // Returns the IP if valid, or default ``` ### Multiple Variables #### First Available Value ```javascript // Use first available variable from a list env.get(['PRIMARY_HOST', 'BACKUP_HOST', 'DEFAULT_HOST']); // With default fallback env.get(['PRIMARY_HOST', 'BACKUP_HOST'], 'localhost'); ``` #### Batch Operations ```javascript // Get multiple values as an array env.getAll(['SECRET', 'HOST', 'PORT']); // Get multiple values as an object with defaults env.getAll({ API_KEY: null, // null means no default PORT: 3000, // Default if not set DEBUG: false }); ``` ### Validation #### Existence Checking ```javascript // Check if variables exist env.ok('HOST'); // true if HOST exists env.ok('HOST', 'PORT', 'API_KEY'); // true if ALL exist ``` #### Assertions ```javascript // Validate variables (throws error if invalid) env.assert( // Simple existence check 'HOST', // Type checking { PORT: { type: 'number' }}, // Custom validation { REFRESH_INTERVAL: { type: 'number', ok: val => val >= 1000 } } ); ``` #### Adding to the environment ```javascript env.set('NEW_ENV_VAR', 'newVal'); process.env.NEW_ENV_VAR // 'newVal' env.get('NEW_ENV_VAR'); // 'newVal' ``` ### AWS Credentials ```javascript // Get AWS credentials from standard environment variables const { awsKeyId, awsSecretAccessKey, awsSessionToken, awsRegion } = env.getAWS(); // With default region const credentials = env.getAWS({ region: 'us-west-2' }); ``` ### AWS Secrets Manager Integration Some folks like to store secrets in AWS secrets manager in the form of a JSON object as opposed (or in addition) to environment variables. It's me, I'm some folks. Good Env now supports this pattern. To avoid introducing a dependency you'll have to bring your own instance of AWS Secrets Manager though. Be sure to specify your AWS region as an environment variable, otherwise, it'll default to `us-east-1`. Not only will your secrets be merged with the Good Env store, but they will also be stored in the underlying `process.env` object in case there are components that are still pulling from the environment directly. Note, if something goes wrong, this function _will_ throw an error. ```javascript const awsSecretsManager = require('@aws-sdk/client-secrets-manager'); (async function() { // Load secrets from AWS Secrets Manager await env.use(awsSecretsManager, 'my-secret-id'); // The secret ID can also be specified via environment variables // AWS_SECRET_ID or SECRET_ID await env.use(awsSecretsManager); // Secrets are automatically merged with existing environment variables // and can be accessed using any of the standard methods const secretValue = env.get('someSecretFromAWSSecretsManager'); }()); ``` ## Important Behavior Notes ### Boolean Existence vs Value When checking for the existence of a boolean environment variable: ```javascript // If A_BOOL_VAL=false env.ok('A_BOOL_VAL'); // Returns true (checking existence, not value) env.getBool('A_BOOL_VAL'); // Returns false (actual value) ``` ### URL Validation - `getUrl()` only supports 'http', 'https', 'redis', and 'postgresql' protocols - Invalid URLs return `null` instead of throwing errors - Using `getUrl()` ensures proper URL format ## Examples ### Complete Configuration Setup ```javascript // app-config.js const env = require('good-env'); // Validate critical variables env.assert( 'DATABASE_URL', { PORT: { type: 'number' }} ); module.exports = { port: env.num('PORT', 3000), database: env.url('DATABASE_URL'), debug: env.bool('DEBUG', false), allowedOrigins: env.list('ALLOWED_ORIGINS', 'localhost'), cache: { enabled: env.bool('CACHE_ENABLED', true), ttl: env.num('CACHE_TTL', 3600) } }; ``` ## License MIT