# good-env

> Better environment variable handling for Twelve-Factor node apps

Latest version **7.8.1** (published 2026-05-13) · MIT license · 0 weekly downloads

## Install

```sh
npm install good-env
pnpm add good-env
yarn add good-env
bun add good-env
```

## Health

**Score 60/100 (C)** — status: active.

Positive: has types; no vulnerabilities; high maintenance score; high quality score.

Warnings: low downloads; no esm support.

## Facts

| | |
|---|---|
| Version | 7.8.1 |
| Published | 2026-05-13 |
| First published | 2016-08-27 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Node | >=18.20.4 |
| Dependencies | 0 |
| Unpacked size | 31.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 16 |
| Author | Johnny Ray Austin |
| Maintainers | recursivefunk |
| Keywords | environment, environment-variables, environment variables, server, env, twelve-factor, twelve factor, 12-factor, 12 factor |

## Links

- npm: https://www.npmjs.com/package/good-env
- Repository: https://github.com/recursivefunk/good-env
- Homepage: https://github.com/recursivefunk/good-env#readme
- Issues: https://github.com/recursivefunk/good-env/issues
- npm.io page: https://npm.io/package/good-env

## Alternatives

- [replicas-cli](https://npm.io/package/replicas-cli.md) — 3.0K weekly downloads
- [env-contract](https://npm.io/package/env-contract.md) — 133 weekly downloads
- [@openveo/api](https://npm.io/package/@openveo/api.md) — 61 weekly downloads
- [@ryniaubenpm2/cumque-error-reiciendis](https://npm.io/package/@ryniaubenpm2/cumque-error-reiciendis.md) — 54 weekly downloads
- [ts-global-type-extra](https://npm.io/package/ts-global-type-extra.md) — 11 weekly downloads

## Recent versions

- 7.8.1 (latest) — 2026-05-13
- 7.8.0 — 2026-04-22
- 7.7.0 — 2026-04-22
- 7.6.3 — 2026-04-22
- 7.6.2 — 2026-04-08
- 7.6.1 — 2025-06-10
- 7.6.0 — 2025-06-10
- 7.5.0 — 2025-06-01
- 7.4.0 — 2025-04-05
- 7.3.0 — 2025-03-08
- 7.2.0 — 2025-01-17
- 7.1.4 — 2025-01-15
- 7.1.3 — 2025-01-11
- 7.1.2 — 2025-01-11
- 7.1.1 — 2024-11-28
- … 33 more at https://npm.io/package/good-env/versions

## README

# 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

---
_Source: https://npm.io/package/good-env · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
