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

env-var-manager

v3.2.0

Published

A tiny helper tool that lets you define a set of environment variables including validation rules

Readme

env-var-manager 🌐

Version

Define, validate, transform, and read environment variables with one small TypeScript-friendly helper.

Installation

npm install env-var-manager

Usage

import { createEnvVarManager } from "env-var-manager";

const envVarManager = createEnvVarManager({
  API_BASE_URL: {
    retrieve: () => process.env.API_BASE_URL,
    transform: (value) => new URL(value),
  },
  PORT: {
    retrieve: () => process.env.PORT,
    transform: (value) => Number.parseInt(value, 10),
    validate: (value) => Number.isInteger(value) && value > 0,
    invalidValueErrorMsg: "PORT must be a positive integer.",
  },
});

envVarManager.validateAll();

const apiBaseUrl = envVarManager.getEnvVar("API_BASE_URL");
const port = envVarManager.getEnvVar("PORT");

validateAll() checks the whole config and throws a summarized error by default. getEnvVar() reads one variable lazily, caches successful transforms, and throws by default when the value is missing or invalid.

If you want to define the config against an explicit string union and get missing-key enforcement, use defineEnvVarConfig():

import { createEnvVarManager, defineEnvVarConfig } from "env-var-manager";

type RequiredEnvVars = "APP_ENV" | "SERVER_URL";

const envVarConfig = defineEnvVarConfig<RequiredEnvVars>()({
  APP_ENV: {
    retrieve: () => process.env.APP_ENV,
    transform: (value) => value as "development" | "production",
  },
  SERVER_URL: {
    retrieve: () => process.env.SERVER_URL,
    transform: (value) => new URL(value),
  },
});

const envVarManager = createEnvVarManager(envVarConfig);

That helper is the recommended way to get both:

  • exact key enforcement for a declared union
  • good editor autocomplete while writing the config object

If you want to handle failures yourself, pass false as the second argument and handle undefined:

const optionalPort = envVarManager.getEnvVar("PORT", false);

Optional variables can be declared explicitly:

const envVarManager = createEnvVarManager({
  OPTIONAL_TOKEN: {
    retrieve: () => process.env.OPTIONAL_TOKEN,
    transform: (value) => value,
    optional: true,
  },
});

Default values are supported and participate in validation:

const envVarManager = createEnvVarManager({
  PORT: {
    retrieve: () => process.env.PORT,
    transform: (value) => Number.parseInt(value, 10),
    defaultValue: 3000,
    validate: (value) => value > 0,
  },
});

Sensitive variables can be redacted from validation output:

const envVarManager = createEnvVarManager({
  API_TOKEN: {
    retrieve: () => process.env.API_TOKEN,
    transform: (value) => value,
    sensitive: true,
  },
});

When sensitive: true is set, validation reports and wrapped transform errors redact the value and error details.

Validation can also depend on other env vars through the validation context:

const envVarManager = createEnvVarManager(
  defineEnvVarConfig<"APP_ENV" | "SERVER_URL">()({
    APP_ENV: {
      retrieve: () => process.env.APP_ENV,
      transform: (value) => value as "development" | "production",
    },
    SERVER_URL: {
      retrieve: () => process.env.SERVER_URL,
      transform: (value) => new URL(value),
      validate: (value, { getEnvVar }) => {
        const appEnv = getEnvVar("APP_ENV");

        if (appEnv === "production" && value.protocol !== "https:") {
          return {
            valid: false,
            errorMessage:
              "SERVER_URL must use https because APP_ENV is production.",
          };
        }

        return { valid: true };
      },
    },
  }),
);

validate supports both styles:

validate: (value) => value > 0
validate: (value, context) => ({
  valid: value > 0,
  errorMessage: value > 0 ? undefined : "PORT must be greater than zero.",
})

Validation error message precedence is:

  • validate returns { valid: false, errorMessage: "..." }: use that message
  • validate returns false: fall back to invalidValueErrorMsg
  • neither provides a message: use the default fallback text in the report

That makes it possible to keep simple validators simple, while still supporting dynamic, context-aware error messages for more complex setups.

Group Constraints

Some setups need relationships between multiple variables, not just per-variable validation.

createEnvVarManager() accepts an optional second argument with groups for that:

import { createEnvVarManager, defineEnvVarConfig } from "env-var-manager";

const envVarConfig = defineEnvVarConfig<
  "API_TOKEN" | "API_TOKEN_FILE" | "AUTH_HEADER"
>()({
  API_TOKEN: {
    retrieve: () => process.env.API_TOKEN,
    transform: (value) => value,
    optional: true,
    sensitive: true,
  },
  API_TOKEN_FILE: {
    retrieve: () => process.env.API_TOKEN_FILE,
    transform: (value) => value,
    optional: true,
  },
  AUTH_HEADER: {
    retrieve: () => process.env.AUTH_HEADER,
    transform: (value) => value,
    optional: true,
  },
});

const envVarManager = createEnvVarManager(envVarConfig, {
  groups: [
    {
      name: "token-input",
      mode: "exactly-one",
      members: ["API_TOKEN", "API_TOKEN_FILE"],
      errorMessage:
        "Choose exactly one token source: API_TOKEN or API_TOKEN_FILE.",
    },
    {
      name: "auth-material",
      mode: "at-least-one",
      members: ["API_TOKEN_FILE", "AUTH_HEADER"],
    },
  ],
});

Supported modes:

  • at-least-one: one or more members must be defined
  • exactly-one: one member must be defined, but not multiple

Important semantics:

  • group membership is typed from your config keys, so editor autocomplete works and unknown keys are rejected
  • one variable can appear in multiple groups
  • group checks are about effective presence, not validator success
  • retrieved values, transformed values, and defaultValue all count as defined for group checks
  • missing optional variables do not count as defined
  • transform failures still count as defined, so exactly-one groups still catch over-specified setups

When group validation fails, validateAll(false, false) returns an invalidGroups array:

const result = envVarManager.validateAll(false, false);

console.log(result.invalidGroups);
// [
//   {
//     name: "token-input",
//     mode: "exactly-one",
//     members: ["API_TOKEN", "API_TOKEN_FILE"],
//     definedMembers: ["API_TOKEN", "API_TOKEN_FILE"],
//     reason: "Choose exactly one token source: API_TOKEN or API_TOKEN_FILE.",
//   },
// ]

That same information is also included in the thrown validation report when you call validateAll() with the default throwError = true.

You can also clear the cache explicitly:

envVarManager.resetCache();

Development

This repository uses npm, not Yarn or pnpm.

Useful commands:

npm test
npm run test:types
npm run build
npm run verify

Commits are expected to use conventional commit messages. Local raw git commit is blocked by Husky on purpose; use the interactive helper instead:

npm run commit

That helper is intentionally a small local script instead of commitizen to avoid extra transitive maintenance and vulnerability surface.

Release Workflow

Releases are automated with semantic-release in GitHub Actions.

  • CI runs npm ci and npm run verify.
  • Releases run on pushes to main or master, plus manual workflow dispatch.
  • The workflow is designed for protected branches and does not rely on pushing version or changelog commits back to git.
  • Git tags, npm releases, and GitHub Releases are the release source of truth.
  • Because of that, the package.json version committed on main can lag behind the latest published version.
  • The release workflow is set up for npm Trusted Publishing via GitHub Actions OIDC. No long-lived NPM_TOKEN should be required once the npm package is configured for trusted publishing on the npm side.

You can preview the release flow locally with:

npm run release:dry-run

The latest published version on npm is expected to be tagged in git as vX.Y.Z. If historical tags are missing, add the tag at the commit that actually shipped instead of relying on the current branch tip.