enwow
v0.1.0
Published
Cross-platform environment variables loader and validator with Standard Schema support
Maintainers
Readme
enwow
Cross-platform environment variables loader and validator with Standard Schema support.
Features
- 🚀 Cross-platform - Works on Node.js, Bun, and Deno
- ✅ Standard Schema - Compatible with Zod, Valibot, ArkType, and more
- 📁 .env file loading - With proper file priority support
- 🔌 Pluggable sources - Load env from JSON files, CLI commands, secret managers, or any custom source
- 🔒 Type-safe - Full TypeScript support with type inference
- 🪶 Lightweight - Minimal dependencies
Installation
npm install enwow
# or
pnpm add enwow
# or
yarn add enwowYou'll also need a schema library that supports Standard Schema:
npm install zod
# or
npm install valibot
# or any other Standard Schema compatible libraryQuick Start
Basic Usage
import { Env } from 'enwow'
import { z } from 'zod'
// Define your schema
const env = await Env.create({
PORT: z.string().transform(Number).default('3000'),
HOST: z.string().default('localhost'),
DATABASE_URL: z.string().url(),
DEBUG: z.string().optional(),
})
// Type-safe access
env.get('PORT') // number
env.get('HOST') // string
env.get('DATABASE_URL') // string
env.get('DEBUG') // string | undefinedWith .env Files
import { Env } from 'enwow'
import { z } from 'zod'
const env = await Env.create(new URL('./', import.meta.url), {
PORT: z.string().transform(Number).default('3000'),
HOST: z.string().default('localhost'),
})
// Values from .env files are loaded and validatedFile Loading Priority
Files are loaded in the following order (highest priority first):
| Priority | File Name | Environment | Notes |
|----------|-----------|-------------|-------|
| 1st | .env.[NODE_ENV].local | Current environment | Loaded when NODE_ENV is set |
| 2nd | .env.local | All | Not loaded in test environment |
| 3rd | .env.[NODE_ENV] | Current environment | Loaded when NODE_ENV is set |
| 4th | .env | All | Always loaded |
process.env always has the highest priority and will override values from any file.
API
Env.create(schema, options?)
Create a new Env instance without loading .env files.
const env = await Env.create({
PORT: z.string().transform(Number),
})Env.create(path, schema, options?)
Create a new Env instance and load .env files from the specified directory.
const env = await Env.create(new URL('./', import.meta.url), {
PORT: z.string().transform(Number),
})Options
interface EnvCreateOptions {
// Ignore existing process.env values
ignoreProcessEnv?: boolean
// Override NODE_ENV for file loading
nodeEnv?: string
// Custom environment variables source
envSource?: Record<string, string | undefined>
// Custom source adapters (cannot be used with path argument)
sources?: EnvSourceAdapter[]
}env.get(key)
Get a validated environment variable value.
const port = env.get('PORT') // Type: numberenv.all()
Get all validated environment variables as an object.
const all = env.all() // Type: { PORT: number, HOST: string, ... }env.has(key)
Check if a variable is defined.
if (env.has('DEBUG')) {
// ...
}Error Handling
When validation fails, an EnvValidationError is thrown:
import { EnvValidationError } from 'enwow'
try {
const env = await Env.create({
REQUIRED_VAR: z.string(),
})
}
catch (error) {
if (error instanceof EnvValidationError) {
console.log('Validation failed:')
for (const issue of error.issues) {
console.log(` ${issue.path}: ${issue.message}`)
}
}
}Sources
By default, enwow loads environment variables from .env files and process.env. With source adapters, you can load from any source — JSON files, CLI commands, secret managers, or custom providers.
import { Env } from 'enwow'
import { fromFiles, fromJSON, fromObject, fromProcessEnv } from 'enwow/adapters'Sources are merged left-to-right: later sources override earlier ones.
Built-in Adapters
| Adapter | Description |
|---------|-------------|
| fromFiles({ directory, ...options }) | Load from .env files (same as the default behavior) |
| fromProcessEnv() | Read from process.env (cross-platform) |
| fromJSON({ path }) | Read from a JSON file with flat key-value structure |
| fromObject({ env }) | Use a static object (useful for testing and defaults) |
Example: Multiple Sources
import { Env } from 'enwow'
import { fromFiles, fromJSON, fromProcessEnv } from 'enwow/adapters'
import { z } from 'zod'
const env = await Env.create({
PORT: z.string().transform(Number).default('3000'),
DATABASE_URL: z.string().url(),
API_SECRET: z.string(),
}, {
sources: [
fromFiles({ directory: new URL('./', import.meta.url) }), // lowest priority
fromJSON({ path: './config/env.json' }),
fromProcessEnv(), // highest priority
],
})Custom Adapter
Use defineSourceAdapter to create a type-safe adapter factory:
import { defineSourceAdapter, fromFiles, fromProcessEnv } from 'enwow/adapters'
interface VaultOptions {
url: string
token: string
}
const vault = defineSourceAdapter<VaultOptions>((opts) => {
return {
name: 'vault',
async load() {
const response = await fetch(opts.url, {
headers: { 'X-Vault-Token': opts.token },
})
const { data } = await response.json()
return data.data
},
}
})
const env = await Env.create(schema, {
sources: [
fromFiles({ directory: new URL('./', import.meta.url) }),
vault({ url: 'https://vault.example.com/v1/secret/data/myapp', token: process.env.VAULT_TOKEN! }),
fromProcessEnv(),
],
})Note: The
sourcesoption cannot be used together with thepathargument inEnv.create(path, schema). UsefromFiles()adapter instead.
Advanced Usage
With Valibot
import { Env } from 'enwow'
import * as v from 'valibot'
const env = await Env.create({
PORT: v.pipe(v.string(), v.transform(Number), v.minValue(1), v.maxValue(65535)),
HOST: v.optional(v.string(), 'localhost'),
})With ArkType
import { type } from 'arktype'
import { Env } from 'enwow'
const env = await Env.create({
PORT: type('string.integer').pipe(Number),
HOST: type('string').default('localhost'),
})Manual Validation
import { EnvValidator, parseEnv } from 'enwow'
import { z } from 'zod'
const validator = new EnvValidator({
PORT: z.string().transform(Number),
})
const result = validator.validate(process.env)Direct File Parsing
import { EnvLoader, EnvParser } from 'enwow'
const loader = new EnvLoader(new URL('./', import.meta.url))
const files = await loader.load()
for (const file of files) {
const parser = new EnvParser(file.contents)
const parsed = parser.parse()
console.log(parsed)
}Contribution
- Clone this repository
- Install the latest LTS version of Node.js
- Enable Corepack using
corepack enable - Install dependencies using
pnpm install - Run tests using
pnpm test
License
MIT License
