node-safe-env
v0.1.3
Published
A tiny schema-based env loader and validator for Node.js.
Maintainers
Readme
node-safe-env
Schema-based environment validation for Node.js and TypeScript.
Validate environment variables at startup, parse them into runtime types, and catch configuration problems before your app boots.
Why node-safe-env?
- Fail fast during application startup.
- Parse raw env strings into typed runtime values.
- See aggregated validation issues in one error report.
- Validate
.envand.env.exampleusage from scripts or CI. - Inspect where values came from with debug tracing.
Features
- Schema-based environment validation
- TypeScript-friendly schema inference
- Automatic parsing for numbers, booleans, arrays, dates
- Nested schemas with flattened env keys
- CLI validation for env and
.env.example - Aggregated validation errors
- Debug tracing for env source resolution
Try it instantly
You can run the CLI without installing the package globally. The quickest way to try node-safe-env is with npx.
npx node-safe-env --helpValidate environment variables:
npx node-safe-env validate --schema ./env.schema.tsValidate .env.example:
npx node-safe-env validate-example --schema ./env.schema.tsInstall
npm install node-safe-envExamples
Runnable examples are available in the repository and mirror common usage patterns.
examples/basic- minimal schema validationexamples/nested- nested schema with flattened env keysexamples/advanced- arrays, custom parsing, debug mode, maskingexamples/cli-schema- schema module intended for CLI validation
Quick Start
Define a schema:
import { createEnv, defineEnv } from "node-safe-env";
const schema = defineEnv({
APP_NAME: { type: "string", required: true },
PORT: { type: "port", default: 3000 },
DEBUG: { type: "boolean", default: false },
NODE_ENV: {
type: "enum",
values: ["development", "test", "production"],
default: "development",
},
} as const);Use it at runtime:
const env = createEnv(schema);
env.APP_NAME; // string
env.PORT; // number
env.DEBUG; // boolean
env.NODE_ENV; // "development" | "test" | "production"What this gives you:
required: truemeans the value must exist.defaultis used when a value is missing.typecontrols parsing and validation.- The returned
envobject is strongly typed from your schema.
Example .env:
APP_NAME=DemoApp
PORT=4000
DEBUG=true
NODE_ENV=developmentValues are read as strings from the environment and then parsed according to your schema.
What Is a Schema in node-safe-env?
A schema is an object where each key maps to a rule object.
Each rule describes:
- What type the value should be with
type - Whether it must be present with
required - What to use if it is missing with
default - Any rule-specific options such as enum values, array settings, or a custom parser
Use defineEnv() when you want better literal type inference and a reusable exported schema module.
// env.schema.ts
import { defineEnv } from "node-safe-env";
export const schema = defineEnv({
PORT: { type: "port", default: 3000 },
ADMIN_EMAIL: { type: "email", required: true },
} as const);When to use node-safe-env
Use this library if you want to:
- validate environment variables at application startup
- enforce typed configuration in Node.js or TypeScript projects
- ensure
.env.examplestays aligned with your configuration schema - detect configuration issues early in CI pipelines
Common Use Cases
- App startup validation: load and validate env once during bootstrap.
- CI checks for
.env.example: fail pull requests when required keys are missing or stale. - Debugging source issues: use debug output to understand where values were loaded from.
Value at a Glance
node-safe-env is a schema-based env validator with:
- TypeScript-friendly schema inference
- CLI commands for runtime and
.env.examplevalidation - Structured, aggregated validation errors
- Source tracing and debug visibility
Nested Schemas
Schemas can be nested, but environment variable names are always flattened. Keys are generated by joining object path segments with _ and converting each segment to uppercase. The library does not convert camelCase to snake_case, so server.allowedHosts becomes SERVER_ALLOWEDHOSTS, not SERVER_ALLOWED_HOSTS.
Examples:
server.port->SERVER_PORTdatabase.url->DATABASE_URLserver.allowedHosts->SERVER_ALLOWEDHOSTS
const env = createEnv({
server: {
port: { type: "port", default: 3000 },
allowedHosts: { type: "array", required: true },
},
database: {
url: { type: "string", required: true },
},
});
console.log(env.server.port);SERVER_PORT=4000
SERVER_ALLOWEDHOSTS=localhost,example.com
DATABASE_URL=postgres://localhost:5432/appCLI
node-safe-env includes two CLI commands:
validate: validate current environment values against your schemavalidate-example: validate.env.examplecoverage against your schema
You can run the CLI without installing the package globally:
npx node-safe-env --helpMost common commands:
npx node-safe-env validate --schema ./env.schema.ts
npx node-safe-env validate-example --schema ./env.schema.tsnode-safe-env validate --schema ./env.schema.ts
node-safe-env validate-example --schema ./env.schema.ts
node-safe-env validate --schema ./dist/env.schema.js --strictCLI schema files
The --schema module supports both JavaScript and TypeScript files:
- JavaScript:
.js,.mjs,.cjs - TypeScript:
.ts,.mts,.cts
Accepted export shapes:
- Default export
- Named export
schema
.env.example Validation
.env.example should contain all keys defined in your schema so new environments and CI checks stay aligned with your application configuration.
Example .env.example:
PORT=3000
DEBUG=false
NODE_ENV=developmentValidate a real file:
import { validateExampleEnvFile } from "node-safe-env";
const issues = validateExampleEnvFile(schema, {
cwd: process.cwd(),
exampleFile: ".env.example",
});Validate an in-memory object:
import { validateExampleEnv } from "node-safe-env";
const issues = validateExampleEnv(schema, {
PORT: "",
});Rule Types
Supported type values:
stringnumberbooleanenumurlportjsonintfloatarrayemaildatecustom
Loading Order
When options.source is not provided, values are merged in this order and later sources win:
.env.env.local.env.<NODE_ENV>- Custom file from
options.envFile process.env
Debug Mode
Use debug mode to inspect how values were loaded and resolved.
import { createEnv, type EnvDebugReport } from "node-safe-env";
const reports: EnvDebugReport[] = [];
createEnv(schema, {
debug: {
logger: (report) => reports.push(report),
},
});Or log debug reports directly:
createEnv(schema, { debug: true });Strict Mode
createEnv(schema, { strict: true });Strict mode reports unknown environment keys as validation issues.
Error Handling
Validation issues are aggregated and thrown as EnvValidationError.
import { createEnv, EnvValidationError } from "node-safe-env";
try {
createEnv(schema);
} catch (error) {
if (error instanceof EnvValidationError) {
console.error(error.issues);
}
}API Summary
Exports:
createEnvdefineEnvEnvValidationErrormaskEnvmergeSourcestraceEnvvalidateExampleEnvvalidateExampleEnvFilereadEnvFileSourceresolveExampleEnvPath
createEnv(schema, options?):
{
source?: Record<string, string | undefined>;
cwd?: string;
nodeEnv?: string;
envFile?: string;
strict?: boolean;
debug?: boolean | { logger?: (report: EnvDebugReport) => void };
}Compatibility
- Node.js >= 18
- ESM and CommonJS builds
- TypeScript declarations included
Development
npm run lint
npm run build
npm run typecheck
npm run test
npm run test:watch
npm run checkLicense
MIT
