strongly-typed-env
v2.2.0
Published
a tiny library for strongly typed environment variables where you can specify the types in the .env file itself, no dependencies
Maintainers
Readme
TypeScript Environment Configuration Library
A powerful, type-safe environment configuration library for Node.js applications that automatically generates TypeScript types from your environment files.
Features
- 🔒 Type-safe environment variables with automatic TypeScript type generation
- 🎯 Multiple data types - STRING, NUMBER, BOOL, ARRAY, OBJ
- 🛡️ Runtime validation with comprehensive error handling
- 📝 Auto-generated documentation with comments and schemas
- 🔍 Duplicate detection and environment validation
- 🚀 Easy integration with existing Node.js projects
- ✨ Precise type inference for objects and arrays with actual property types
Installation
npm install strongly-typed-envQuick Start
1. Create your environment file
Create a .env file with typed environment variables:
⚠️ Warning : Make sure to use the correct data types for each variable and each variable should start and end on a single line.
# Database Configuration
STRING DATABASE_URL = "postgresql://localhost:5432/mydb"
NUMBER DATABASE_PORT = 5432
BOOL DATABASE_SSL = true
# API Configuration
STRING API_KEY = "your-secret-api-key"
NUMBER MAX_CONNECTIONS = 100
ARRAY ALLOWED_ORIGINS = ["http://localhost:3000", "https://example.com"]
OBJ REDIS_CONFIG = {"host": "localhost", "port": 6379, "retryDelayOnFailover": 100}
# Feature Flags
BOOL ENABLE_LOGGING = true
BOOL DEBUG_MODE = false
# Advanced Examples
ARRAY PORT_NUMBERS = [3000, 4000, 5000]
ARRAY MIXED_ARRAY = ["string", 42, true]
OBJ SERVER_CONFIG = {"host": "localhost","port": 8080,"ssl": true,"timeout": 30000,"features": ["auth", "logging", "metrics"]}2. Generate TypeScript types
Add a script to your package.json:
{
"scripts": {
"generate-env-types": "node -e \"require('strongly-typed-env').generateTypes('.env', './src/types/env-types.ts')\""
}
}Run the type generation:
npm run generate-env-typesThis generates a ./src/types/env-types.ts file with precise types:
/**
* Auto-generated TypeScript types for environment variables
* Generated from: .env
* Generated at: 2024-01-15T10:30:00.000Z
*
* DO NOT EDIT THIS FILE MANUALLY
* Run generateTypes() to regenerate when .env changes
*/
export interface EnvConfig {
/** Type: STRING */
DATABASE_URL: string;
/** Type: NUMBER */
DATABASE_PORT: number;
/** Type: BOOL */
DATABASE_SSL: boolean;
/** Type: STRING */
API_KEY: string;
/** Type: NUMBER */
MAX_CONNECTIONS: number;
/** Type: ARRAY */
ALLOWED_ORIGINS: string[];
/** Type: OBJ */
REDIS_CONFIG: {
host: string;
port: number;
retryDelayOnFailover: number;
};
/** Type: BOOL */
ENABLE_LOGGING: boolean;
/** Type: BOOL */
DEBUG_MODE: boolean;
/** Type: ARRAY */
PORT_NUMBERS: number[];
/** Type: ARRAY */
MIXED_ARRAY: (string | number | boolean)[];
/** Type: OBJ */
SERVER_CONFIG: {
host: string;
port: number;
ssl: boolean;
timeout: number;
features: string[];
};
}
export const envSchema = {
DATABASE_URL: 'STRING',
DATABASE_PORT: 'NUMBER',
DATABASE_SSL: 'BOOL',
API_KEY: 'STRING',
MAX_CONNECTIONS: 'NUMBER',
ALLOWED_ORIGINS: 'ARRAY',
REDIS_CONFIG: 'OBJ',
ENABLE_LOGGING: 'BOOL',
DEBUG_MODE: 'BOOL',
PORT_NUMBERS: 'ARRAY',
MIXED_ARRAY: 'ARRAY',
SERVER_CONFIG: 'OBJ',
} as const;
export type EnvKey = keyof EnvConfig;
export type EnvType = 'STRING' | 'NUMBER' | 'BOOL' | 'ARRAY' | 'OBJ';
// Utility function to get environment variable keys
export const envKeys: EnvKey[] = [
'DATABASE_URL',
'DATABASE_PORT',
'DATABASE_SSL',
'API_KEY',
'MAX_CONNECTIONS',
'ALLOWED_ORIGINS',
'REDIS_CONFIG',
'ENABLE_LOGGING',
'DEBUG_MODE',
'PORT_NUMBERS',
'MIXED_ARRAY',
'SERVER_CONFIG',
];
// Type guard to check if a key exists in the environment config
export function isEnvKey(key: string): key is EnvKey {
return envKeys.includes(key as EnvKey);
}3. Use in your application
import { config, validateEnv } from 'your-env-config-package';
import { EnvConfig, envSchema } from './types/env-types';
// Load and parse environment variables
const { parsedEnv } = config<EnvConfig>({
path: '.env',
strict: true,
});
// Validate against schema
if (validateEnv(parsedEnv, envSchema)) {
// Now parsedEnv is fully typed as EnvConfig with precise types
console.log(`Database URL: ${parsedEnv.DATABASE_URL}`);
console.log(`Port: ${parsedEnv.DATABASE_PORT}`);
console.log(`SSL enabled: ${parsedEnv.DATABASE_SSL}`);
console.log(`Allowed origins: ${parsedEnv.ALLOWED_ORIGINS.join(', ')}`); // string[] methods available
console.log(`Redis host: ${parsedEnv.REDIS_CONFIG.host}`); // Typed object properties
console.log(`Redis port: ${parsedEnv.REDIS_CONFIG.port}`); // IntelliSense knows this is a number
console.log(`Port numbers: ${parsedEnv.PORT_NUMBERS.map((p) => p * 2)}`); // number[] methods
console.log(
`Server features: ${parsedEnv.SERVER_CONFIG.features.length} features`,
); // Nested array typing
} else {
console.error('Environment validation failed');
process.exit(1);
}API Reference
generateTypes(envPath?, outputPath?, options?)
Generates TypeScript types from an environment file with precise type inference for objects and arrays.
Parameters:
envPath(string, optional): Path to the .env file. Default:'.env'outputPath(string, optional): Output path for generated types. Default:'./src/types/env-types.ts'options(object, optional):interfaceName(string): Name of the generated interface. Default:'EnvConfig'includeComments(boolean): Include comments in generated file. Default:trueexportSchema(boolean): Export schema constant. Default:true
Example:
import { generateTypes } from 'your-env-config-package';
generateTypes('.env.production', './src/types/prod-env.ts', {
interfaceName: 'ProductionEnv',
includeComments: false,
exportSchema: true,
});config<T>(options?)
Parses environment variables and returns a typed object.
Parameters:
options(object, optional):path(string): Path to environment file. Default:'.env'encoding(BufferEncoding): File encoding. Default:'utf8'strict(boolean): Throw on errors vs warn. Default:false
Returns: { parsedEnv: T }
validateEnv<T>(env, schema)
Validates environment variables against a schema.
Parameters:
env(Record<string, EnvValue>): Environment variables to validateschema(Record<string, EnvType>): Schema to validate against
Returns: boolean - Type guard that narrows env to type T
createTypedConfig<T>()
Creates a typed config function for a specific interface.
Example:
import { createTypedConfig } from 'your-env-config-package';
import { EnvConfig } from './types/env-types';
const getConfig = createTypedConfig<EnvConfig>();
const { parsedEnv } = getConfig({ strict: true });Supported Types
| Type | TypeScript Type | Example Value | Generated Type Example |
| -------- | ------------------------- | --------------------------------------------- | --------------------------------------------------------- |
| STRING | string | "hello world" | string |
| NUMBER | number | 42, 3.14 | number |
| BOOL | boolean | true, false | boolean |
| ARRAY | Precise array types | [1, 2, 3], ["a", "b"], [1, "a", true] | number[], string[], (number \| string \| boolean)[] |
| OBJ | Precise object interfaces | {"name": "John", "age": 30, "active": true} | { name: string; age: number; active: boolean } |
Advanced Type Examples
Complex Objects
OBJ DATABASE_CONFIG = {
"connections": {
"read": {"host": "read-db", "port": 5432},
"write": {"host": "write-db", "port": 5432}
},
"pool": {"min": 5, "max": 20},
"ssl": true,
"features": ["logging", "metrics"]
}Generates:
DATABASE_CONFIG: {
connections: {
read: { host: string; port: number };
write: { host: string; port: number };
};
pool: { min: number; max: number };
ssl: boolean;
features: string[];
}Mixed Arrays
ARRAY API_ENDPOINTS = [
{"path": "/users", "methods": ["GET", "POST"]},
{"path": "/orders", "methods": ["GET", "PUT", "DELETE"]}
]Generates:
API_ENDPOINTS: Array<{
path: string;
methods: string[];
}>;Environment File Format
# Comments are supported
TYPE VARIABLE_NAME = value
# Examples:
STRING API_URL = "https://api.example.com"
NUMBER PORT = 3000
BOOL DEBUG = true
ARRAY HOSTS = ["localhost", "example.com"]
ARRAY PORTS = [3000, 4000, 5000]
ARRAY MIXED = ["string", 42, true, {"nested": "object"}]
OBJ CONFIG = {"timeout": 5000, "retries": 3, "enabled": true}
OBJ COMPLEX_CONFIG = {
"database": {"host": "localhost", "port": 5432},
"cache": {"ttl": 300, "enabled": true},
"features": ["auth", "logging"]
}Rules:
- Variable names must match pattern:
^[a-zA-Z_][a-zA-Z0-9_]*$ - Strings can be quoted or unquoted
- Arrays and objects must be valid JSON
- Boolean values must be
trueorfalse(case-insensitive) - Comments start with
# - Empty lines are ignored
- Nested objects and arrays are fully typed
Error Handling
The library provides comprehensive error handling:
try {
const { parsedEnv } = config<EnvConfig>({ strict: true });
} catch (error) {
if (error.message.includes('Environment file not found')) {
console.error('Please create a .env file');
} else if (error.message.includes('Invalid number value')) {
console.error('Check your numeric values in .env');
} else {
console.error('Environment parsing failed:', error.message);
}
}Development Workflow
Recommended package.json scripts:
{
"scripts": {
"generate-env-types": "node -e \"require('./dist/index.js').generateTypes()\"",
"generate-env-types:dev": "node -e \"require('./dist/index.js').generateTypes('.env.development', './src/types/dev-env.ts')\"",
"generate-env-types:prod": "node -e \"require('./dist/index.js').generateTypes('.env.production', './src/types/prod-env.ts')\"",
"validate-env": "node -e \"const {config,validateEnv}=require('./dist/index.js');const {envSchema}=require('./src/types/env-types.js');const {parsedEnv}=config();console.log('✅ Environment valid:', validateEnv(parsedEnv,envSchema));\""
}
}Git hooks integration:
Add to your .husky/pre-commit or similar:
#!/bin/sh
npm run generate-env-types
git add src/types/env-types.tsBest Practices
- Keep .env files out of version control (add to
.gitignore) - Commit generated type files for team consistency
- Use environment-specific files (
.env.development,.env.production) - Validate environment on application startup
- Use meaningful variable names and group related variables
- Document complex object/array structures in comments
- Leverage precise typing for better IntelliSense and runtime safety
Examples
Advanced Usage with Full Type Safety
import { config, validateEnv, createTypedConfig } from 'strongly-typed-env';
import { EnvConfig, envSchema } from './types/env-types';
// Create environment-specific configs
const getDevConfig = createTypedConfig<EnvConfig>();
const getProdConfig = createTypedConfig<EnvConfig>();
function loadConfig(): EnvConfig {
const environment = process.env.NODE_ENV || 'development';
const envFile = `.env.${environment}`;
const { parsedEnv } = config<EnvConfig>({
path: envFile,
strict: true,
});
if (!validateEnv(parsedEnv, envSchema)) {
throw new Error(`Invalid environment configuration in ${envFile}`);
}
return parsedEnv;
}
export const env = loadConfig();
// Usage with full type safety
console.log(`Database host: ${env.DATABASE_CONFIG.host}`); // TypeScript knows this is a string
console.log(`Connection pool max: ${env.DATABASE_CONFIG.pool.max}`); // TypeScript knows this is a number
env.API_ENDPOINTS.forEach((endpoint) => {
// TypeScript provides full IntelliSense for the endpoint object
console.log(`${endpoint.path}: ${endpoint.methods.join(', ')}`);
});Type Safety Benefits
- IntelliSense: Full autocomplete for object properties and array methods
- Compile-time checking: Catch type errors before runtime
- Refactoring safety: Rename properties with confidence across your codebase
- Documentation: Types serve as living documentation for your environment structure
- Runtime safety: Combined with validation, ensures type correctness at runtime
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
MIT License - see LICENSE file for details.
