E N V T Y P E S
Your code knows what env vars it needs. Now your tooling does too.
You deploy to production. The app crashes. DATABASE_URL was missing. Someone added it three weeks ago and nobody updated .env.production.
envtypes makes this impossible. It scans your code, finds every process.env reference, infers types from naming conventions, catches security issues, and generates type-safe access — all automatically.
Unlike envalid, znv, or t3-env, you don't manually define schemas. envtypes reads your codebase and figures it out.
$ npx envtypes doctor
Framework: next
Scanning /app (45ms)
Found 12 variables in 8 files
Validation
✓ .env
Security
✗ CRITICAL NEXT_PUBLIC_API_SECRET is client-exposed but contains "SECRET"
→ Move to a server-only variable without the client prefix
! WARNING JWT_SECRET has a weak or placeholder default value
→ Remove the default and require explicit configuration
Sync
✓ .env.example is in sync
───────────────────────────────────
1 critical issue(s) 1 warning(s)
───────────────────────────────────
npx envtypes doctorOne command. No config. Instant report.
npm install -D @stulevtoday/envtypes| Command | What it does |
|---|---|
envtypes scan |
Find all env vars in your code |
envtypes check |
Validate .env files against your code |
envtypes doctor |
Full health check: validation + security + sync |
envtypes generate |
Emit a type-safe env.ts module + .env.example |
envtypes diff |
Compare two .env files with masked secrets |
envtypes compare |
Matrix view across multiple environments |
envtypes audit |
Full markdown/JSON report for docs & compliance |
envtypes watch |
Continuous validation during development |
envtypes migrate |
Import from envalid, znv, or t3-env |
envtypes hook |
Install git pre-commit validation hook |
envtypes init |
Generate .envtypes.ts schema from scan |
Every command that outputs text supports --json for CI pipelines.
Type-safe env access with full TypeScript inference:
import { defineEnv, t } from "@stulevtoday/envtypes";
const env = defineEnv({
PORT: t.port().default("3000"),
DATABASE_URL: t.url().description("Primary database"),
NODE_ENV: t.enum(["development", "production", "test"]),
DEBUG: t.boolean().optional(),
WORKERS: t.integer().default("4"),
ADMIN_EMAIL: t.email(),
FEATURE_FLAGS: t.json<{ dark: boolean }>().optional(),
BUILD_TAG: t.regex(/^v\d+\.\d+\.\d+$/, "semver").optional(),
});
env.PORT // number
env.NODE_ENV // "development" | "production" | "test"
env.FEATURE_FLAGS // { dark: boolean } | undefinedFails fast on startup with all errors at once:
Error: Environment validation failed:
- DATABASE_URL: Expected valid URL, got "not-a-url"
- ADMIN_EMAIL: Expected email address, got "nope"
| Builder | Parses to | Validates |
|---|---|---|
t.string() |
string |
any value |
t.number() |
number |
numeric |
t.integer() |
number |
whole numbers |
t.boolean() |
boolean |
true/false/1/0/yes/no |
t.port() |
number |
0-65535 |
t.url() |
string |
valid URL |
t.email() |
string |
valid email |
t.enum([...]) |
union type | exact match |
t.json<T>() |
T |
valid JSON |
t.regex(re) |
string |
pattern match |
All types support .optional(), .default("value"), and .description("text").
envtypes uses ts-morph to parse your source files and find every env var reference:
process.env.PORT // dot access
process.env["API_KEY"] // bracket access
const { REDIS_URL } = process.env // destructuring
import.meta.env.VITE_API_URL // Vite / Astro
Deno.env.get("DATABASE_URL") // Deno
Bun.env.SECRET_KEY // BunDefault values are detected from ||, ??, destructuring defaults, and ternary expressions.
| Pattern | Inferred type |
|---|---|
*_PORT |
port (0-65535) |
*_URL, *_URI |
URL |
*_EMAIL, SMTP_FROM |
|
DEBUG, ENABLE_*, IS_*, USE_* |
boolean |
*_COUNT, *_SIZE, *_TIMEOUT |
number |
NODE_ENV |
enum |
| everything else | string |
Automatically identifies your framework and classifies variables as client-exposed or server-only:
| Framework | Client prefix | Detection |
|---|---|---|
| Next.js | NEXT_PUBLIC_ |
next in deps |
| Vite | VITE_ |
vite in deps |
| Astro | PUBLIC_ |
astro in deps |
| Nuxt | NUXT_PUBLIC_ |
nuxt in deps |
| CRA | REACT_APP_ |
react-scripts in deps |
| Expo | EXPO_PUBLIC_ |
expo in deps |
| Remix | (none) | @remix-run/* in deps |
envtypes catches real mistakes:
- Client-exposed secrets —
NEXT_PUBLIC_API_SECRETwill be visible in the browser - Leaked connection strings —
VITE_DATABASE_URLexposes credentials to the client - Weak defaults —
JWT_SECRETdefaults to"changeme" - Known credentials —
AWS_SECRET_ACCESS_KEY,STRIPE_SECRET_KEY, etc. - Missing gitignore —
.envfiles not covered by.gitignore
See which variables are present or missing across environments at a glance:
$ envtypes compare .env .env.staging .env.production
Variable .env .env.staging .env.production
──────────────────────────────────────────────────────────────
API_KEY sk****yz sk****ab sk****cd
DATABASE_URL postgres://… postgres://… postgres://…
DEBUG true ✗ ✗
NODE_ENV development staging production
PORT 3000 3000 8080
──────────────────────────────────────────────────────────────
Coverage: .env: 5/5 (100%) · .env.staging: 4/5 (80%) · .env.production: 4/5 (80%)
Production-grade parser included:
# Standard
PORT=3000
# With export prefix
export DATABASE_URL=postgres://localhost/db
# Multiline (double quotes)
RSA_KEY="-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----"
# Escape sequences
MSG="line 1\nline 2\ttabbed"
# Variable interpolation
DB_HOST=localhost
DB_PORT=5432
DATABASE_URL=postgres://${DB_HOST}:${DB_PORT}/mydb
# Inline comments
TIMEOUT=5000 # millisecondsComing from another tool? One command:
npx envtypes migrate # auto-detects envalid, znv, or t3-env
npx envtypes migrate --dry-run # preview first- name: Validate environment
run: npx envtypes doctor --ci- uses: stulevtoday/envtypes@v1
with:
command: doctornpx envtypes hook installBlocks commits when env issues are found. Remove with npx envtypes hook uninstall.
Optional. Create .envtypes.json or add "envtypes" to package.json:
{
"include": ["src/**/*.ts", "lib/**/*.ts"],
"exclude": ["**/*.test.*"],
"output": "src/env.ts",
"ignore": ["LEGACY_VAR"],
"overrides": {
"CUSTOM_PORT": { "type": "port" },
"LOG_LEVEL": { "type": "enum", "enumValues": ["debug", "info", "warn", "error"] }
}
}import { scan, generateSchema, validate, parseEnvFile, detectFrameworks, analyzeSecurityIssues, defineEnv, t } from "@stulevtoday/envtypes";MIT