@chaisser/env-validator
v1.0.1
Published
Validate environment variables with schema
Maintainers
Readme
@chaisser/env-validator
Validate environment variables with schema
Validate, parse, and transform environment variables using a declarative schema. Supports string, number, boolean, URL, email, port, enum, JSON, regex, and date types.
Features
- Schema-driven validation with type coercion
- 10 built-in types: string, number, boolean, url, email, port, enum, json, regex, date
- Required/optional fields with defaults
- Min/max constraints, pattern matching, custom transforms
.envfile parsing (no dependencies)- Schema composition: merge, pick, omit, partial, required
- Sensitive value masking and redaction
- Detailed error reporting
- Full TypeScript support
Installation
npm install @chaisser/env-validator
# or
yarn add @chaisser/env-validator
# or
pnpm add @chaisser/env-validatorQuick Start
import { validateEnv, parseEnvFile } from '@chaisser/env-validator';
import { readFileSync } from 'fs';
const schema = {
DATABASE_URL: { type: 'url', required: true },
PORT: { type: 'port', default: 3000 },
NODE_ENV: {
type: 'enum',
values: ['development', 'production', 'test'],
default: 'development'
},
DEBUG: { type: 'boolean', default: false },
MAX_CONNECTIONS: { type: 'number', min: 1, max: 100, default: 10 }
};
// From process.env
const result = validateEnv(schema);
// From .env file
const env = parseEnvFile(readFileSync('.env', 'utf-8'));
const result2 = validateEnv(schema, env);
if (!result.valid) {
console.error(result.errors);
process.exit(1);
}
console.log(result.env.PORT); // 3000 (number)
console.log(result.env.DEBUG); // false (boolean)
console.log(result.env.NODE_ENV); // 'development' (string)API Reference
Validation
| Function | Description |
|----------|-------------|
| validateEnv(schema, env?) | Validate env against schema. Returns { valid, env, errors } |
| validateEnvOrThrow(schema, env?) | Validate and throw on failure. Returns typed env object |
| parseEnv(schema, env?) | Alias for validateEnvOrThrow |
| validateEnvStrict(schema, env?) | Validate and also flag extra keys not in schema |
| isEnvValid(schema, env?) | Returns boolean |
| getEnvErrors(schema, env?) | Returns EnvValidationError[] |
| validateValue(key, entry, value) | Validate a single value against a schema entry |
Schema Entry
interface EnvSchemaEntry {
type: 'string' | 'number' | 'boolean' | 'url' | 'email' | 'port' | 'enum' | 'json' | 'regex' | 'date';
required?: boolean;
default?: unknown;
values?: unknown[]; // for 'enum'
min?: number; // for 'number'
max?: number; // for 'number'
pattern?: RegExp; // for 'string'
transform?: (value: string) => unknown;
description?: string;
sensitive?: boolean; // marks for masking
}Parse Helpers
| Function | Input | Output |
|----------|-------|--------|
| parseBoolean(value) | 'true', '1', 'yes', 'on' | true |
| parseNumber(value) | '42', '3.14' | number |
| parsePort(value) | '443' | number (0-65535) |
| parseJSON(value) | '{"a":1}' | parsed value |
| parseDate(value) | '2024-01-15' | Date |
Check Helpers
| Function | Returns true for |
|----------|-------------------|
| isUrl(value) | Valid URLs |
| isEmail(value) | Valid email addresses |
| isPort(value) | Integer strings 0-65535 |
| isJSON(value) | Valid JSON strings |
| isRegex(value) | Valid regex patterns |
| isDate(value) | Parseable date strings |
Schema Inspection
| Function | Returns |
|----------|---------|
| getSchemaKeys(schema) | All schema keys |
| getRequiredKeys(schema) | Keys marked required: true |
| getOptionalKeys(schema) | Keys without required |
| getSensitiveKeys(schema) | Keys marked sensitive: true |
| getEnvDefaults(schema) | Default values as object |
| getEnvDocumentation(schema) | Human-readable docs array |
Schema Manipulation
| Function | Description |
|----------|-------------|
| mergeSchemas(...schemas) | Merge multiple schemas (later wins) |
| extendSchema(base, extension) | Extend base with new entries |
| pickSchema(schema, keys) | Keep only specified keys |
| omitSchema(schema, keys) | Remove specified keys |
| partialSchema(schema) | Make all fields optional |
| requiredSchema(schema) | Make all fields required |
Env Manipulation
| Function | Description |
|----------|-------------|
| maskEnv(env, sensitiveKeys?) | Replace sensitive values with ab****yz |
| redactEnv(env, keys) | Replace values with [REDACTED] |
| sanitizeEnv(env) | Remove undefined/null entries |
.env File Parsing
| Function | Description |
|----------|-------------|
| parseEnvFile(content) | Parse .env file content string into key-value object |
Supports KEY=value, comments (#), quoted values, and inline comments.
Error Formatting
| Function | Description |
|----------|-------------|
| formatErrors(errors) | Format errors as readable lines |
| createErrorReport(result) | Generate detailed error report |
Diff & Check
| Function | Description |
|----------|-------------|
| diffEnv(schema, env?) | Returns { missing, extra, valid } key arrays |
| hasEnvKey(key, env?) | Check if a key is set and non-empty |
Examples
Validate and Throw
import { validateEnvOrThrow } from '@chaisser/env-validator';
const env = validateEnvOrThrow({
API_KEY: { type: 'string', required: true },
PORT: { type: 'port', default: 3000 }
}, process.env as Record<string, string>);
// env.API_KEY is typed as unknown
// env.PORT is typed as unknown (default: 3000)Enum with Required Values
const env = validateEnvOrThrow({
LOG_LEVEL: {
type: 'enum',
values: ['error', 'warn', 'info', 'debug'],
default: 'info'
}
}, { LOG_LEVEL: 'debug' });
// env.LOG_LEVEL === 'debug'Number with Constraints
const result = validateEnv({
TIMEOUT: { type: 'number', min: 100, max: 30000, default: 5000 },
WORKERS: { type: 'number', min: 1, max: 16, default: 4 }
}, { TIMEOUT: '1000', WORKERS: '32' });
// result.valid === false (WORKERS exceeds max)
// result.errors[0].message === '"WORKERS" must be <= 16'Custom Transform
const env = validateEnvOrThrow({
ALLOWED_ORIGINS: {
type: 'string',
transform: (v) => v.split(',').map(s => s.trim())
}
}, { ALLOWED_ORIGINS: 'http://localhost:3000, https://example.com' });
// env.ALLOWED_ORIGINS === ['http://localhost:3000', 'https://example.com']Schema Composition
import { mergeSchemas, pickSchema, partialSchema } from '@chaisser/env-validator';
const baseSchema = {
NODE_ENV: { type: 'enum', values: ['dev', 'prod'], required: true },
PORT: { type: 'port', default: 3000 }
};
const dbSchema = {
DATABASE_URL: { type: 'url', required: true },
DB_POOL_SIZE: { type: 'number', min: 1, max: 50, default: 5 }
};
const fullSchema = mergeSchemas(baseSchema, dbSchema);
// Only validate database config
const dbOnly = pickSchema(fullSchema, ['DATABASE_URL', 'DB_POOL_SIZE']);
// Make everything optional (e.g., for partial updates)
const optionalSchema = partialSchema(fullSchema);Masking Sensitive Values
import { maskEnv, getSensitiveKeys } from '@chaisser/env-validator';
const schema = {
API_KEY: { type: 'string', required: true, sensitive: true },
DATABASE_URL: { type: 'url', required: true, sensitive: true },
PORT: { type: 'port', default: 3000 }
};
const env = validateEnvOrThrow(schema, { API_KEY: 'sk-abc123xyz', DATABASE_URL: 'postgres://...', PORT: '8080' });
const masked = maskEnv(env, getSensitiveKeys(schema));
// { API_KEY: 'sk****yz', DATABASE_URL: 'po****..', PORT: 8080 }
console.log(masked); // Safe to logParse .env File
import { parseEnvFile, validateEnv } from '@chaisser/env-validator';
import { readFileSync } from 'fs';
const content = `
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
# Auth
API_KEY="sk-abc123-def456"
SECRET_KEY='my-secret-key'
# App
NODE_ENV=production
DEBUG=false
`;
const parsed = parseEnvFile(content);
// { DB_HOST: 'localhost', DB_PORT: '5432', ..., DEBUG: 'false' }
const result = validateEnv({
DB_HOST: { type: 'string', required: true },
DB_PORT: { type: 'port', default: 5432 },
NODE_ENV: { type: 'enum', values: ['development', 'production', 'test'] },
DEBUG: { type: 'boolean', default: false }
}, parsed);Strict Mode (Detect Extra Keys)
import { validateEnvStrict } from '@chaisser/env-validator';
const result = validateEnvStrict(
{ PORT: { type: 'port', default: 3000 } },
{ PORT: '8080', UNKNOWN_VAR: 'surprise' }
);
// result.valid === false
// result.extraKeys === ['UNKNOWN_VAR']License
MIT
