npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zod-envkit

v1.5.0

Published

Validate environment variables with Zod and generate .env.example

Readme

Type-safe environment variable validation and documentation using Zod.

zod-envkit is a small, explicit library + CLI that treats environment variables as an
explicit runtime contract, not an implicit guessing game.

  • validate process.env at startup
  • get fully typed environment variables
  • generate .env.example
  • generate documentation (ENV.md, ENV.json, ENV.yaml)
  • inspect env state via CLI (with secret masking)
  • validate env strictly in CI/CD
  • bootstrap configuration via zod-envkit init
  • load multiple .env* files with priority

No cloud. No magic. Just code.


Why

Environment variables are critical, but are usually handled poorly.

Typical problems:

  • process.env is just string | undefined
  • missing or invalid variables fail at runtime
  • .env.example and docs drift out of sync
  • CI/CD breaks late and unpredictably

zod-envkit solves this by making env:

  • validated early
  • typed
  • documented
  • checkable in CI

When to use

Use zod-envkit if:

  • you want env errors to fail at startup, not in production
  • you use TypeScript and care about correct types
  • you want .env.example and docs from a single source of truth
  • you want CI to catch missing / unknown variables

When NOT to use

Skip zod-envkit if:

  • your project is extremely small and informal
  • you don’t control environment variables at all
  • you expect automatic schema introspection or “magic” behavior

Installation

npm install zod-envkit
yarn add zod-envkit
pnpm add zod-envkit
bun add zod-envkit

Library usage (runtime validation)

Create a single file responsible for loading and validating env.

import "dotenv/config";
import { z } from "zod";
import { loadEnv, mustLoadEnv, formatZodError } from "zod-envkit";

const EnvSchema = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]),
  PORT: z.coerce.number().int().min(1).max(65535),
  DATABASE_URL: z.string().url(),
});

Safe mode (no throw)

const result = loadEnv(EnvSchema);

if (!result.ok) {
  console.error("Invalid environment:\n" + formatZodError(result.error));
  process.exit(1);
}

export const env = result.env;

Fail-fast mode (recommended)

export const env = mustLoadEnv(EnvSchema);

Now:

  • env.PORT is a number
  • env.DATABASE_URL is a string
  • TypeScript knows everything at compile time
  • the app fails fast if env is invalid

CLI usage

The CLI works from a metadata file: env.meta.json.

By default, it is searched in:

  • ./env.meta.json
  • ./examples/env.meta.json

Example env.meta.json

{
  "NODE_ENV": {
    "description": "Runtime mode",
    "example": "development",
    "required": true
  },
  "PORT": {
    "description": "HTTP port",
    "example": "3000",
    "required": true
  },
  "DATABASE_URL": {
    "description": "Postgres connection string",
    "example": "postgresql://user:pass@localhost:5432/db",
    "required": true
  }
}

CLI commands

Generate .env.example and documentation

(Default behavior)

npx zod-envkit

or explicitly:

npx zod-envkit generate

Generate docs in different formats:

npx zod-envkit generate --format json
npx zod-envkit generate --format yaml

Control sorting:

npx zod-envkit generate --sort alpha
npx zod-envkit generate --sort required-first

Show current environment status

Loads dotenv files, masks secrets, and displays a readable table.

npx zod-envkit show

Advanced options:

npx zod-envkit show --mask-mode full
npx zod-envkit show --no-mask
npx zod-envkit show --dotenv ".env,.env.local,.env.production"

Validate environment (CI-friendly)

npx zod-envkit check

Strict mode (fail on unknown variables):

npx zod-envkit check --strict

Production guard (stricter deploy/CI checks; unknown dotenv keys fail like --strict):

npx zod-envkit check --production
  • exits with code 1 if required variables are missing
  • in --strict mode also fails on unknown variables (dotenv-loaded keys only)
  • with --production also fails on unknown dotenv variables (same dotenv-only scope as --strict)

Initialize configuration

Bootstrap configuration from existing files.

Generate env.meta.json from .env.example:

npx zod-envkit init

Generate .env.example from existing metadata:

npx zod-envkit init --from-meta

Stability & Versioning

zod-envkit follows Semantic Versioning.

Public API stability (1.x)

Everything listed below is treated as stable public API for the whole 1.x line.

Library exports (entrypoint zod-envkit):

  • loadEnv
  • mustLoadEnv
  • formatZodError
  • checkEnv
  • getMissingEnv
  • getUnknownEnv
  • isSecretKey
  • generateEnvExample
  • generateEnvDocs
  • sortMetaEntries
  • related public types: EnvMeta, EnvMetaEntry, EnvCheckResult, GenerateDocsOptions, DocsFormat, SortMode

CLI contract:

  • commands: generate, show, check, init
  • documented flags and defaults
  • exit code behavior (success = 0, user error = 1)

Breaking change policy

A breaking change (major) includes:

  • changing signatures or return shapes of the stable exports
  • removing/renaming public exports
  • removing/renaming CLI commands or flags
  • changing CLI default behavior (e.g. what zod-envkit does with no args)
  • changing exit code semantics
  • changing output format contracts in a way that breaks existing tooling

A non-breaking change (minor/patch) includes:

  • adding new exports (backwards compatible)
  • adding new CLI flags (backwards compatible)
  • adding new optional fields to env.meta.json
  • improving error messages or docs output while keeping it valid

What’s new in 1.2.0

Version 1.2.0 focuses on reliability, contract enforcement, and CI readiness.

Highlights:

  • hardened strict mode behavior (check --strict) for CI usage
  • deterministic dotenv priority handling
  • improved secret masking guarantees in show
  • expanded preprod test suite (smoke, contract, CLI E2E, robustness)
  • CI pipeline enforces: build → tests → docs build

This is the point where zod-envkit stops being “just a helper” and becomes a stable environment contract tool for CI/CD pipelines.


Why not just dotenv?

dotenv:

  • ❌ no validation
  • ❌ no types
  • ❌ no documentation
  • ❌ no CI checks

zod-envkit:

  • ✅ validation
  • ✅ TypeScript inference
  • ✅ documentation
  • ✅ CLI tooling

They are designed to be used together.


Design principles

  • explicit configuration over magic
  • no framework coupling
  • small and predictable API
  • library and CLI are independent but complementary
  • environment variables are a runtime contract

License

MIT