good-env
Version:
Better environment variable handling for Twelve-Factor node apps
278 lines (201 loc) ⢠7.58 kB
Markdown
# good-env
<p align="center">
<img src="./img/good-env-logo.svg" alt="good-env-logo" width="400"/>
</p>

[](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.
[](https://www.npmjs.com/package/good-env)
[](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