@harshkhandelwalcs/env-doctor
v0.1.0
Published
Validate that your environment variables actually work — not just that they exist
Maintainers
Readme
env-doctor
Validate that your environment variables actually work — not just that they exist.
Most tools check that DATABASE_URL is present. env-doctor checks that it can connect to the database.
env-doctor v0.1.0
────────────────────────────────────────────────────
DATABASE_URL ✓ passed (23ms) PostgreSQL 16.2
REDIS_URL ✗ ECONNREFUSED — Connection refused
→ Redis is not running. Start it: redis-server
OPENAI_API_KEY ✗ HTTP 401 Unauthorized — API key rejected
→ Check your API key is correct and has not expired
JWT_SECRET ✓ passed HMAC-SHA256 compatible, 64 chars
NODE_ENV ✓ passed "production"
PORT ✗ Invalid port: "abc"
→ Must be an integer between 1 and 65535
────────────────────────────────────────────────────
✗ 3 of 6 failed 3 passed (487ms)Why?
| Tool | What it checks |
|------|---------------|
| dotenv | Loads .env into process.env |
| dotenv-safe | Checks required keys are present |
| zod / envalid | Validates types and formats |
| env-doctor | Validates that the services actually respond |
"Works on my machine" is usually a broken environment. env-doctor catches that before your app crashes in production.
Install
npm install -D @harshkhandelwalcs/env-doctorNo required peer deps. Only install what you need:
npm install pg # for check.postgres.connect() npm install ioredis # for check.redis.ping() npm install nodemailer # for check.smtp.connect() npm install mongodb # for check.mongo.connect() npm install mysql2 # for check.mysql.connect()
Quick Start
1. Generate a config:
npx env-doctor init > env.doctor.js2. Edit env.doctor.js:
// env.doctor.js
import { check } from '@harshkhandelwalcs/env-doctor'
export default {
DATABASE_URL: check.postgres.connect(),
REDIS_URL: check.redis.ping(),
JWT_SECRET: check.crypto.hmacable({ minLength: 32 }),
API_URL: check.http.reachable(),
NODE_ENV: check.format.oneOf('development', 'production', 'test'),
}3. Run it:
npx env-doctor4. Add to your package.json so it runs before every dev session:
{
"scripts": {
"predev": "env-doctor",
"dev": "node server.js"
}
}All Checks
check.postgres.connect(options?)
Connects to a PostgreSQL database and runs SELECT version().
DATABASE_URL: check.postgres.connect({ timeout: 5000 })
// DATABASE_URL=postgres://user:pass@localhost:5432/mydbcheck.redis.ping(options?)
Sends PING to Redis and expects PONG.
REDIS_URL: check.redis.ping({ timeout: 3000 })
// REDIS_URL=redis://localhost:6379
// REDIS_URL=redis://:password@localhost:6379check.mongo.connect(options?)
Connects to MongoDB and runs buildInfo.
MONGODB_URI: check.mongo.connect()
// MONGODB_URI=mongodb://localhost:27017/mydbcheck.mysql.connect(options?)
Connects to MySQL/MariaDB and runs SELECT VERSION().
MYSQL_URL: check.mysql.connect()
// MYSQL_URL=mysql://user:pass@localhost:3306/mydbcheck.smtp.connect(options?)
Authenticates with an SMTP server using nodemailer.verify().
SMTP_URL: check.smtp.connect()
// SMTP_URL=smtp://user:[email protected]:587
// SMTP_URL=smtps://user:[email protected]:465check.http.reachable(options?)
Makes a GET request and passes for any HTTP < 500 response (server is up).
API_URL: check.http.reachable({ timeout: 5000 })check.http.status(code, options?)
Expects an exact HTTP status code.
HEALTH_URL: check.http.status(200)check.http.bearerAuth(url, options?)
Validates an API key by calling a URL with it as a Bearer token.
Fails if the server returns 401 or 403.
OPENAI_API_KEY: check.http.bearerAuth('https://api.openai.com/v1/models'),
GITHUB_TOKEN: check.http.bearerAuth('https://api.github.com/user'),
STRIPE_API_KEY: check.http.bearerAuth('https://api.stripe.com/v1/customers?limit=1'),check.crypto.hmacable(options?)
Validates a secret is long and usable as an HMAC-SHA256 key.
JWT_SECRET: check.crypto.hmacable({ minLength: 32 })check.crypto.minLength(n)
SESSION_SECRET: check.crypto.minLength(64)check.crypto.base64()
ENCRYPTION_KEY: check.crypto.base64()check.crypto.jwt()
Validates structural JWT format (3 base64url parts).
REFRESH_TOKEN: check.crypto.jwt()check.format.oneOf(...values)
NODE_ENV: check.format.oneOf('development', 'production', 'test')
LOG_LEVEL: check.format.oneOf('debug', 'info', 'warn', 'error')check.format.url()
SITE_URL: check.format.url()check.format.email()
ADMIN_EMAIL: check.format.email()check.format.port(options?)
PORT: check.format.port({ min: 1024, max: 65535 })check.format.uuid()
TENANT_ID: check.format.uuid()check.format.regex(pattern, hint?)
AWS_REGION: check.format.regex(/^[a-z]{2}-[a-z]+-\d$/, 'e.g. us-east-1')Chaining Checks
Pass an array to run checks in sequence — stops at first failure:
ENCRYPTION_KEY: [
check.crypto.notEmpty(),
check.crypto.minLength(32),
check.crypto.base64(),
],CLI Options
npx env-doctor [options]
Options:
-c, --config <path> Path to config file (default: env.doctor.js)
-e, --env <file> .env file to load (default: .env)
--timeout <ms> Timeout per check (default: 10000)
--json Output results as JSON
--no-exit Do not exit with code 1 on failure
-V, --version Show version
-h, --help Show help
Commands:
run (default) Run all checks
init Print a starter env.doctor.js to stdoutProgrammatic API
import { run, check } from '@harshkhandelwalcs/env-doctor'
const summary = await run({
DATABASE_URL: check.postgres.connect(),
JWT_SECRET: check.crypto.hmacable({ minLength: 32 }),
}, {
envFile: '.env.test', // which .env file to load
timeout: 5000, // ms per check
exitOnFail: false, // don't call process.exit
})
console.log(summary.passed.length) // 1
console.log(summary.failed.length) // 0
console.log(summary.duration) // total ms
for (const result of summary.failed) {
console.log(result.key, result.result.error, result.result.hint)
}CI / CD Integration
GitHub Actions:
- name: Check environment
run: npx env-doctor --json
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}Exit codes:
0— all checks passed1— one or more checks failed
Is It Worth It?
Backend developers (Node.js, Express, Fastify, NestJS)
High value. Every check is relevant:
- DB connections catch wrong credentials, wrong host, DB not created
- Redis checks catch missing password, wrong port
- SMTP checks catch firewall blocking port 587
- Secret checks catch JWT secrets that are too short
Frontend developers (React, Vue, Vite, Next.js)
Medium value — mainly useful checks:
check.http.reachable()for your API base URLcheck.http.bearerAuth()for third-party API keys (OpenAI, Stripe, etc.)check.format.url()forVITE_API_URL,NEXT_PUBLIC_*varscheck.format.oneOf()forNODE_ENV
Full-stack (Next.js, Nuxt, Remix, SvelteKit)
Highest value. You have both — API routes with DB access AND frontend env vars. Add env-doctor to your predev script and never debug a broken env again.
License
MIT
