@shreyas-patil/env-guard
v1.1.0
Published
Runtime .env validation — typed, zero-deps, with coercion, async custom validators, and .env.example generation
Maintainers
Readme
env-guard 🛡️
Runtime .env validation for Node.js. Zero dependencies.
"name": "@shreyas-patil/env-guard",Stop your app from starting with bad config — get a clear, actionable error instead of a cryptic crash.
env-guard: 2 environment variable errors found
✖ DATABASE_URL
→ is required but was not provided
✖ JWT_SECRET
→ must be at least 32 characters (got 9)
received: toosho****
Check your .env file and fix the above issues.Why env-guard?
| Feature | dotenv | zod | envalid | env-guard |
|---|---|---|---|---|
| Loads .env | ✅ | ❌ | ✅ | ✅ |
| Type coercion | ❌ | ✅ | ✅ | ✅ |
| Custom validators | ❌ | ✅ | ✅ | ✅ |
| Async validators | ❌ | ❌ | ❌ | ✅ |
| Secret masking | ❌ | ❌ | ❌ | ✅ |
| .env.example CLI | ❌ | ❌ | ❌ | ✅ |
| Pattern hints in example | ❌ | ❌ | ❌ | ✅ |
| Zero dependencies | ✅ | ❌ | ❌ | ✅ |
| Browser/Edge safe | ✅ | ✅ | ❌ | ✅ |
env-guard = lightweight + zero deps + typed errors + secret masking + CLI
Quick start
// src/env.js — import this everywhere instead of process.env
const { guard } = require('env-guard');
module.exports = guard({
PORT: { type: 'port', default: 3000 },
DATABASE_URL: { type: 'url', description: 'PostgreSQL connection URL' },
JWT_SECRET: { type: 'string', minLength: 32 },
DEBUG: { type: 'boolean', default: false },
NODE_ENV: {
type: 'string',
enum: ['development', 'test', 'production'],
default: 'development',
},
}, { exitOnError: true });// app.js
const env = require('./src/env');
console.log(env.PORT); // number → 3000
console.log(env.DEBUG); // boolean → falseAPI
guard(schema, options) — synchronous, throws on error
const { guard } = require('env-guard');
const env = guard(schema, {
envPath?: string | false, // Path to .env. false = skip file loading. Default: ./.env
failFast?: boolean, // Stop after first error. Default: false
exitOnError?: boolean, // Print errors and process.exit(1). Default: false
overrides?: object, // Injected after process.env. Great for tests.
});safeGuard(schema, options) — no throw, returns result object
const { safeGuard } = require('env-guard');
const result = safeGuard(schema);
if (!result.success) {
console.error(result.errors); // [{ key, message, received? }]
} else {
console.log(result.data.PORT);
}guardAsync(schema, options) — async custom validators
const { guardAsync } = require('env-guard');
const env = await guardAsync({
STRIPE_KEY: {
type: 'string',
custom: async (v) => {
const ok = await verifyStripeKey(v);
return ok || 'STRIPE_KEY is invalid or revoked';
},
},
});generateExample(schema, outputPath) — write .env.example
const { generateExample } = require('env-guard');
const schema = require('./src/env.schema');
generateExample(schema, '.env.example');Schema reference
Every field requires type. Everything else is optional.
Common properties
| Property | Type | Description |
|---|---|---|
| type | string | Required. See types below. |
| description | string | Written as a comment in .env.example. |
| required | boolean | Defaults to true. |
| default | any | Used when variable is missing. |
| example | string | Shown in generated .env.example. |
| custom | function | Custom validator (see below). |
Types
{ type: 'string', minLength, maxLength, pattern, enum }
{ type: 'number', min, max }
{ type: 'boolean' } // accepts true/false/1/0/yes/no/on/off (case-insensitive)
{ type: 'url', protocols: ['http:', 'https:'] }
{ type: 'email' }
{ type: 'port', min, max } // integer 1–65535
{ type: 'json' } // parsed with JSON.parseCustom validators
Return true / undefined to pass. Return false or a string to fail:
API_KEY: {
type: 'string',
custom: (v) => v.startsWith('sk_') || 'Must start with sk_',
},
// Async — verify against a live server
STRIPE_KEY: {
type: 'string',
custom: async (v) => {
const valid = await verifyWithStripe(v);
return valid || 'Key is invalid or revoked';
},
},The custom function always receives the coerced value — a number for type: 'number', a boolean for type: 'boolean', etc.
Secret masking
Keys containing secret, token, key, password, auth, or credential automatically have their value masked in error output:
✖ JWT_SECRET
→ must be at least 32 characters (got 9)
received: toosho**** ← never leaks the full secretGenerating .env.example
CLI
npx env-guard env.schema.js
npx env-guard env.schema.js --output=.env.example.ciSchema file must export a plain object:
// env.schema.js
module.exports = {
PORT: { type: 'port', default: 3000 },
DATABASE_URL: { type: 'url', description: 'PostgreSQL URL' },
JWT_SECRET: { type: 'string', minLength: 32, pattern: /^[a-zA-Z0-9_-]{32,}$/ },
};Generated output:
# ─── Auto-generated by env-guard ─────────────────────────────────────────────
# Do not commit real secrets. Fill in values before running the application.
# HTTP server port
# Type: port | optional
# Default: 3000
PORT=3000
# PostgreSQL URL
# Type: url | required
DATABASE_URL=
# Type: string | required
# Pattern: /^[a-zA-Z0-9_-]{32,}$/ ← pattern hint auto-included
# Length: min 32 chars
JWT_SECRET=Programmatic
const { generateExample } = require('env-guard');
generateExample(require('./env.schema'), '.env.example');Testing
Use overrides to inject env vars without touching process.env:
const { safeGuard } = require('env-guard');
const schema = require('./src/env.schema');
test('valid config passes', () => {
const result = safeGuard(schema, {
envPath: false,
overrides: {
DATABASE_URL: 'postgres://localhost/test',
JWT_SECRET: 'a-sufficiently-long-secret-for-testing',
},
});
expect(result.success).toBe(true);
});
test('missing DATABASE_URL is caught', () => {
const result = safeGuard(schema, { envPath: false });
const keys = result.errors.map(e => e.key);
expect(keys).toContain('DATABASE_URL');
});Browser / Edge runtime
guard() detects non-server environments (typeof window !== 'undefined') and skips validation entirely, returning process.env as-is. This prevents crashes in Next.js Client Components or Edge functions where fs is unavailable.
Publishing checklist
# 1. Set your name in package.json
# 2. Bump version: 1.0.0
npm login
npm publish --access publicLicense
MIT#
