@ironfighter23/env-guard
v1.1.2
Published
Zero-dependency environment variable validator with type coercion, full TypeScript inference, ESM + CJS support, and beautiful dev-time error reporting.
Maintainers
Readme
env-guard
Zero-dependency environment variable validator for Node.js.
Define a schema once — get type coercion, validation, and beautiful error output at startup.
The problem
Every Node.js app reads from process.env. But process.env gives you raw strings — no types, no validation, no defaults. This leads to:
- Silent failures when
PORTisundefined(or the string"undefined") - Type bugs:
ENABLE_CACHE === "false"is truthy in JS - Missing variables discovered at runtime, not startup
- No documentation of what variables your app actually needs
- No
.env.exampleto onboard new developers
Most teams write the same boilerplate fix over and over. env-guard solves it once.
Features
- Zero dependencies — pure Node.js, works in any environment
- Type coercion — strings become numbers, booleans, parsed JSON, validated URLs
- Schema-first — define variables once, get docs for free
- All errors at once — see every missing/invalid variable in a single error, not one at a time
- Beautiful error output — colour-coded, with hints and examples right in the terminal
.env.examplegenerator — auto-generate from your schema- Built-in
.envloader — no dotenv needed - Fluent API — chain
.min(),.max(),.matches(),.default(),.sensitive(),.deprecated() - CLI tool —
npx env-guard --checkvalidates before deploy
Install
npm install @ironfighter23/env-guardQuick start
// config.js — run this at the very top of your app
const { guard, schema } = require('@ironfighter23/env-guard');
const config = guard({
PORT: schema.port().default(3000),
DATABASE_URL: schema.url().sensitive(),
NODE_ENV: schema.enum(['development', 'staging', 'production']),
SECRET_KEY: schema.string().min(32).sensitive(),
ENABLE_CACHE: schema.boolean().default(false),
});
// config is now frozen, type-safe, and fully validated:
// config.PORT → 3000 (number)
// config.ENABLE_CACHE → false (boolean)
// config.DATABASE_URL → "postgresql://..."
module.exports = config;If any variable is missing or invalid, env-guard fails fast with a clear error before your app starts:
╔═ env-guard: invalid environment ══════════════════╗
✖ DATABASE_URL
"DATABASE_URL" is required but was not set
hint: PostgreSQL connection string
example: DATABASE_URL=postgresql://user:pass@localhost:5432/db
✖ SECRET_KEY
SECRET_KEY length must be >= 32 (got 10)
╚══════════════════════════════════════════════════╝
Run "npx env-guard --check" to validate your .env fileAPI Reference
guard(schema, options?)
Validates process.env (or a custom env object) against a schema. Returns a frozen config object with coerced values. Throws EnvGuardError if validation fails.
const config = guard(schemaMap, {
env: process.env, // custom env source (default: process.env)
bail: false, // stop at first error (default: false = collect all)
quiet: false, // suppress success log (default: false)
strict: false, // fail on undeclared variables (default: false)
});Schema types
| Method | Input | Output | Notes |
|--------|-------|--------|-------|
| schema.string() | any string | string | |
| schema.number() | "3.14" | 3.14 | float |
| schema.integer() | "42" | 42 | rejects floats |
| schema.boolean() | "true"/"1"/"yes"/"on" | true | case-insensitive |
| schema.port() | "3000" | 3000 | 1–65535 |
| schema.url() | "https://..." | "https://..." | uses URL constructor |
| schema.email() | "[email protected]" | "[email protected]" | basic format check |
| schema.host() | "localhost" | "localhost" | hostname or IP |
| schema.json() | '{"a":1}' | { a: 1 } | parsed JSON |
| schema.enum([...]) | "dev" | "dev" | must be in list |
Field modifiers
Chain these onto any schema type:
schema.string()
.optional() // not required
.default('hello') // default value (implies optional)
.describe('A description shown in .env.example and error hints')
.example('my-value') // shown in .env.example
.sensitive() // value redacted in any output
.deprecated('Use NEW_VAR instead') // warns if the variable is set
.validate((value, name) => {
// throw an Error to fail validation
if (!value.startsWith('sk-')) throw new Error(`${name} must start with "sk-"`);
})
.min(5) // min value (number) or min length (string)
.max(100) // max value (number) or max length (string)
.matches(/^v\d+$/) // must match regexloadEnv(path?, options?)
Parse and load a .env file into process.env. No dotenv needed.
const { loadEnv } = require('@ironfighter23/env-guard');
loadEnv('.env'); // load .env
loadEnv('.env.local', { override: true }); // override existing vars
loadEnv('.env', { silent: true }); // don't throw if missingSupports:
KEY=valueKEY="value with spaces"KEY='single quoted'export KEY=value- Inline comments:
KEY=value # this is ignored \nescape in double-quoted strings
generateExample(schemaMap)
Generate .env.example content from a schema:
const { generateExample } = require('@ironfighter23/env-guard');
const schemaMap = require('./env.schema.js');
const content = generateExample(schemaMap);
console.log(content);
// # .env.example — generated by env-guard
// # PORT
// # HTTP server port | required
// PORT=8080
// ...parseEnvFile(content)
Parse a .env file string into a plain object (does NOT write to process.env):
const { parseEnvFile } = require('@ironfighter23/env-guard');
const parsed = parseEnvFile(fs.readFileSync('.env', 'utf8'));
// { PORT: '3000', NODE_ENV: 'development', ... }CLI
# Validate your .env against a schema
npx env-guard --check
# Custom paths
npx env-guard --check --env .env.local --schema config/env.schema.js
# Generate a .env.example from your schema
npx env-guard --generate
# Print .env.example to stdout
npx env-guard --example
# List all validators and modifiers
npx env-guard --docsSchema file for CLI
Create env.schema.js in your project root:
const { schema } = require('@ironfighter23/env-guard');
module.exports = {
PORT: schema.port().default(3000).describe('HTTP server port'),
DATABASE_URL: schema.url().describe('Postgres connection string').sensitive(),
NODE_ENV: schema.enum(['development', 'staging', 'production']),
SECRET_KEY: schema.string().sensitive().min(32),
};Framework recipes
Express.js
// server.js
require('@ironfighter23/env-guard').loadEnv(); // load .env first
const { guard, schema } = require('@ironfighter23/env-guard');
const express = require('express');
const config = guard({
PORT: schema.port().default(3000),
NODE_ENV: schema.enum(['development', 'production']).default('development'),
DATABASE_URL: schema.url().sensitive(),
});
const app = express();
app.listen(config.PORT, () => {
console.log(`Server running on port ${config.PORT}`);
});Next.js
// lib/config.js
const { guard, schema } = require('@ironfighter23/env-guard');
module.exports = guard({
// Server-side only
DATABASE_URL: schema.url().sensitive(),
JWT_SECRET: schema.string().min(32).sensitive(),
// Exposed to client (NEXT_PUBLIC_ prefix)
NEXT_PUBLIC_API_URL: schema.url().describe('Public API base URL'),
NEXT_PUBLIC_APP_NAME: schema.string().default('My App'),
});With TypeScript
import { guard, schema } from '@ironfighter23/env-guard';
const config = guard({
PORT: schema.port().default(3000),
DATABASE_URL: schema.url(),
NODE_ENV: schema.enum(['development', 'production'] as const),
ENABLE_CACHE: schema.boolean().default(false),
});
// Full automatic inference — zero casting needed:
// config.PORT → number
// config.NODE_ENV → 'development' | 'production'
// config.ENABLE_CACHE → booleanIntegration with CI/CD
Add a pre-deploy validation step:
# .github/workflows/deploy.yml
- name: Validate environment
run: npx env-guard --check --env .env.production --strict
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
NODE_ENV: productionComparison
| Feature | env-guard | dotenv | envalid | zod (manual) | |---------|-----------|--------|---------|-------------| | Zero dependencies | ✅ | ✅ | ❌ | ❌ | | Type coercion | ✅ | ❌ | ✅ | ✅ | | Collect all errors | ✅ | ❌ | ✅ | ✅ | | .env loader built-in | ✅ | ✅ | ❌ | ❌ | | .env.example generator | ✅ | ❌ | ❌ | ❌ | | CLI validator | ✅ | ❌ | ❌ | ❌ | | Fluent builder API | ✅ | ❌ | partial | ❌ | | Frozen result | ✅ | ❌ | ❌ | ❌ | | Deprecated warnings | ✅ | ❌ | ❌ | ❌ |
Contributing
Pull requests are welcome. Please:
- Fork the repo and create a feature branch
- Add tests for any new feature (
test/index.test.js) - Run
npm test— all tests must pass - Open a PR with a clear description
License
MIT © Nishant Bhatte
