2am. Slack notification. Production app is down.
The error? Cannot read property 'split' of undefined
Turns out ALLOWED_ORIGINS wasn't set in production. The app started fine. Health checks passed. Then the first CORS request came in and crashed the server.
Or that time STRIPE_SECRET_KEY had a trailing space from copy-paste. App started. First payment request? Failed. Took 30 minutes to figure out.
Or when someone set ENABLE_CACHING=false as a string, but the code checked if (config.ENABLE_CACHING) - string "false" is truthy, so caching stayed on.
This stuff happens constantly.
The real problem isn't missing env vars
It's that your app doesn't know they're wrong until it tries to use them.
const port = process.env.PORT || 3000
const apiKey = process.env.API_KEY
const enableCache = process.env.ENABLE_CACHING
const origins = process.env.ALLOWED_ORIGINS?.split(',')
// App starts fine
// apiKey is undefined
// enableCache is the string "false" (truthy!)
// origins will crash when undefined
// Everything looks good... until it's not
Your app starts. Health checks pass. Then 5 minutes later, it tries to call the API and crashes because apiKey is undefined.
Or your boolean flag is the string "false" but your code checks if (enableCache) - string "false" is truthy, so the logic is backwards.
Or someone has a trailing space in STRIPE_KEY=" sk_live_123 " and all payment requests fail with cryptic auth errors.
Fail fast, not later
What if your app refused to start unless everything was valid?
import { env } from 'envconfig-kit'
const config = env({
PORT: { type: 'number', default: 3000 },
API_KEY: { type: 'string' },
DATABASE_URL: { type: 'url' },
})
Now if API_KEY is missing or DATABASE_URL isn't a valid URL, your app won't start at all.
❌ Environment validation failed:
API_KEY: Missing required environment variable
➜ Fix: Add to .env file
Example: API_KEY=your-value-here
STRIPE_SECRET_KEY: Value contains leading/trailing whitespace
➜ Fix: Remove extra spaces
Current: " sk_live_123 "
Expected: "sk_live_123"
Crash at startup, not during runtime. Get useful error messages, not undefined is not a function.
What you get
Type safety
config.PORT // number
config.API_KEY // string
config.DEBUG // boolean
config.REDIS_URL // string | undefined (if optional)
TypeScript knows the exact types. No more process.env.PORT as number.
Built-in .env loading
No need for dotenv. Automatically loads from .env, .env.local, .env.production, etc.
const config = env({
PORT: { type: 'number' }
})
// Just works. No extra setup.
Transform values
Parse arrays, trim whitespace, normalize URLs:
const config = env({
ALLOWED_ORIGINS: {
type: 'string',
transform: (value) => value.split(',').map(s => s.trim())
},
API_URL: {
type: 'url',
transform: (value) => value.replace(/\/$/, '') // remove trailing slash
}
})
config.ALLOWED_ORIGINS // ['http://localhost:3000', 'https://app.com']
Validation types that matter
-
string,number,boolean- the basics -
url- validates protocol, catcheslocalhostandhttp:/missing-slash -
email- actual email validation -
port- number between 0-65535
Real example
Here's what a typical API config looks like:
import { env } from 'envconfig-kit'
export const config = env({
// Server
PORT: { type: 'port', default: 3000 },
HOST: { type: 'string', default: '0.0.0.0' },
NODE_ENV: { type: 'string', default: 'development' },
// Database
DATABASE_URL: { type: 'url' },
// Redis (optional)
REDIS_URL: { type: 'url', optional: true },
// API Keys
API_KEY: { type: 'string' },
JWT_SECRET: { type: 'string' },
// External services
STRIPE_KEY: { type: 'string' },
SENDGRID_KEY: { type: 'string' },
// CORS
ALLOWED_ORIGINS: {
type: 'string',
default: 'http://localhost:3000',
transform: (value) => value.split(',')
},
// Feature flags
ENABLE_ANALYTICS: { type: 'boolean', default: false },
})
// Now everything is typed and validated
// App won't start if anything is wrong
CLI tools
Generate .env.example for your team:
npx envconfig-kit generate
Validate before deployment:
npx envconfig-kit check
Health check:
npx envconfig-kit doctor
Install
npm install envconfig-kit
Zero dependencies. 3KB minified. Works with CommonJS and ESM.
Stop debugging undefined errors at 2am. Make your app fail fast with clear error messages.
Have environment variable horror stories? Drop them in the comments.
Top comments (0)