dotenv-gad is an environment variable validation library that brings type safety, schema validation, and runtime checks to your Node.js, Bun, and JavaScript applications. It works with any environment variable source — .env files, platform dashboards (Vercel, Railway, Docker), CI/CD pipelines, or process.env directly.
- Type-safe environment variables with full IntelliSense
- Schema validation (string, number, boolean, url, email, ip, port, json, array, object)
- At-rest encryption — transparent decrypt at runtime
- Schema composition for modular configs
- Automatic documentation and
.env.examplegeneration - First-class TypeScript support
- CLI tooling (check, sync, types, init, fix, docs, keygen, encrypt, decrypt, rotate, verify, status)
- Sensitive value management and redaction
- Vite plugin with client-safe filtering and HMR
npm install dotenv-gad
# or
yarn add dotenv-gad
# or
pnpm add dotenv-gad
# or
bun add dotenv-gad- Create a schema file (
env.schema.ts):
import { defineSchema } from "dotenv-gad";
export default defineSchema({
PORT: {
type: "number",
default: 3000,
docs: "Port to run the server on",
},
DATABASE_URL: {
type: "string",
required: true,
sensitive: true,
},
});- Validate your environment:
import { loadEnv } from "dotenv-gad";
import schema from "./env.schema";
const env = loadEnv(schema);
console.log(`Server running on port ${env.PORT}`);loadEnv reads from both process.env and your .env file (if present). This means it works out of the box on platforms like Vercel, Railway, Docker, and AWS Lambda where variables are injected into process.env directly — no .env file required.
Full documentation is available at kasimlyee.github.io/dotenv-gad.
| Command | Description |
|---|---|
check |
Validate .env against schema |
sync |
Generate/update .env.example |
types |
Generate env.d.ts TypeScript types |
init |
Create starter schema |
fix |
Fix environment issues interactively |
docs |
Generate .env documentation |
keygen |
Generate an X25519 key pair for encryption |
encrypt |
Encrypt plaintext values for encrypted:true fields |
decrypt |
Print or write back decrypted values |
rotate |
Rotate keys: decrypt → new pair → re-encrypt |
status |
Show encryption status of each schema field |
verify |
Dry-run: confirm all encrypted values decrypt |
npx dotenv-gad check
npx dotenv-gad keygen
npx dotenv-gad encryptThe Vite plugin validates environment variables at build time and exposes a typed, client-safe subset to your browser code via a virtual module.
// vite.config.ts
import { defineConfig } from "vite";
import dotenvGad from "dotenv-gad/vite";
export default defineConfig({
plugins: [
dotenvGad({
schemaPath: "./env.schema.ts",
// clientPrefix: "VITE_", // default — keys matching this prefix are exposed
// publicKeys: [], // additional non-prefixed keys to expose
// generatedTypes: true, // generate .d.ts for IntelliSense
}),
],
});import { env } from "dotenv-gad/client";
console.log(env.VITE_API_URL); // Full type safety & autocomplete- Build-time validation — environment checked every dev/build cycle
- Client-safe filtering — only
VITE_*prefixed variables (or custompublicKeys) exposed to browser - Sensitive protection — variables marked
sensitive: trueare always excluded - Auto-generated types —
dotenv-gad.d.tsgives full IntelliSense onenv. - HMR support — hot reload on
.envor schema changes during development - SSR safety — server-side code gets the full env, not the filtered subset
- Type checking (string, number, boolean, array, object, url, email, ip, port, json, date)
- Required/optional fields with defaults
- Custom validation functions
- Value transforms
- Environment-specific rules
{
API_URL: { type: 'url' },
EMAIL: { type: 'email' },
CONFIG: { type: 'json' },
TAGS: {
type: 'array',
items: { type: 'string' }
}
}Merge multiple schemas for modular configuration:
import { defineSchema, composeSchema } from "dotenv-gad";
const baseSchema = defineSchema({
NODE_ENV: { type: "string", default: "development" },
});
const dbSchema = defineSchema({
DATABASE_URL: { type: "string", required: true, sensitive: true },
});
const schema = composeSchema(baseSchema, dbSchema);{
API_KEY: {
type: 'string',
sensitive: true, // masked in errors, excluded from .env.example
validate: (val) => val.startsWith('sk_')
}
}Store secrets as encrypted ciphertext in .env using asymmetric X25519 + ChaCha20-Poly1305. Only .env.keys (gitignored) can decrypt them.
1. Mark fields as encrypted:
export default defineSchema({
DATABASE_URL: { type: 'string', required: true, sensitive: true, encrypted: true },
API_SECRET: { type: 'string', required: true, sensitive: true, encrypted: true },
});2. Generate keys and encrypt:
npx dotenv-gad keygen # writes ENVGAD_PUBLIC_KEY to .env, creates .env.keys
npx dotenv-gad encrypt # replaces plaintext secrets with encrypted:v1:… tokens3. loadEnv decrypts transparently at runtime:
const env = loadEnv(schema); // reads .env.keys, decrypts, validates — all in one step
console.log(env.DATABASE_URL); // "postgres://user:pass@host/db"Your .env (with encrypted values and the public key) is now safe to commit. See the Encryption guide for key rotation, CI/CD setup, and security details.
Group related variables into a single validated object:
const schema = defineSchema({
DATABASE: {
type: "object",
envPrefix: "DATABASE_", // optional; defaults to 'DATABASE_'
properties: {
DB_NAME: { type: "string", required: true },
PORT: { type: "port", default: 5432 },
PWD: { type: "string", sensitive: true },
},
},
});Given DATABASE_DB_NAME=mydb, DATABASE_PORT=5432, DATABASE_PWD=supersecret:
const env = loadEnv(schema);
// { DATABASE: { DB_NAME: 'mydb', PORT: 5432, PWD: 'supersecret' } }dotenv-gad has support for Bun! All features work seamlessly with Bun's runtime.
bunx dotenv-gad check
bunx dotenv-gad keygen
bunx dotenv-gad encryptimport { loadEnv } from "dotenv-gad";
import schema from "./env.schema";
const env = loadEnv(schema);
console.log(`Server running on port ${env.PORT}`);
// Start a Bun HTTP server
Bun.serve({
port: env.PORT,
fetch() {
return new Response("Hello from Bun with dotenv-gad!");
},
});import express from "express";
import { loadEnv } from "dotenv-gad";
import schema from "./env.schema";
const env = loadEnv(schema);
const app = express();
app.listen(env.PORT, () => {
console.log(`Server running on port ${env.PORT}`);
});const { loadEnv } = require("dotenv-gad");
const schema = require("./env.schema");
const env = loadEnv(schema);
module.exports = {
env: {
API_URL: env.API_URL,
},
};Environment validation failed:
- DATABASE_URL: Missing required environment variable
- PORT: Must be a number (received: "abc")
- API_KEY: Must start with 'sk_' (received: "invalid")
Sensitive values are always masked in error output. Use includeRaw for local debugging:
const env = loadEnv(schema, { includeRaw: true });
// or with finer control
import { EnvValidator } from "dotenv-gad";
const validator = new EnvValidator(schema, {
includeRaw: true,
includeSensitive: true,
});{
PASSWORD: {
type: 'string',
validate: (val) => val.length >= 8,
error: 'Password must be at least 8 characters'
}
}{
FEATURES: {
type: 'array',
transform: (val) => val.split(',')
}
}{
DEBUG: {
type: 'boolean',
env: {
development: { default: true },
production: { default: false }
}
}
}MIT © [Kasim Lyee]
Contributions are welcome!