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

schema-env

v2.1.6

Published

Type-safe environment variable validation for Node.js using Zod schemas or custom adapters. Load .env files, expand variables, fetch async secrets, and validate process.env at startup.

Readme

schema-env: Your App's Smart Instruction Checker!

Ever tried to build a LEGO set without the right pieces or with confusing instructions? Your app can feel the same way if its "environment variables" (special settings it needs to run) are wrong!

schema-env is like a super-helpful assistant that checks these settings for your Node.js app before it even starts. It makes sure everything is A-OK, so your app can run smoothly and reliably.


TL;DR (Too Long; Didn't Read)

schema-env makes your app safer by checking its settings (like API keys, port numbers) against a rulebook (your Zod schema or custom adapter) right at the start. It can read settings from .env files, special files for development/production, and even secret vaults! If something's wrong, it tells you immediately.


DX Highlights (Developer Experience Wins!)

  • Peace of Mind: No more "Oops, I forgot that setting!" errors in production.
  • 📖 Clear Rules: Define exactly what your app needs, in one place.
  • 🤝 Team-Friendly: Everyone knows what settings are required.
  • 🤖 Async & Flexible: Works with modern setups, including fetching secrets.
  • 🧩 Use Your Favorite Tools: Zod is built-in, but you can plug in Joi, Yup, etc.
  • 💡 Smart & Simple API: Easy to get started, powerful when you need it.

What's an "Environment Variable"? And Why Check Them?

Think of environment variables as little notes you give your app:

  • PORT=3000 (Tells your app which door to use for web traffic)
  • API_KEY=supersecret123 (A secret password to talk to another service)
  • NODE_ENV=development (Tells your app if it's in "practice mode" or "live mode")

If these notes are missing, misspelled, or have the wrong kind of info (like text where a number should be), your app might get confused, crash, or even worse, do something unexpected!

schema-env helps by:

  1. Reading a "Rulebook" (Schema): You tell schema-env what notes your app expects and what they should look like.
  2. Checking the "Notes" (.env files & system): It looks at the notes you've provided.
  3. Giving a Thumbs Up or Down: If all notes match the rulebook, great! If not, it stops your app and tells you exactly what's wrong.

This makes your app:

  • 👍 More Reliable: Fewer surprise crashes.
  • 🔒 More Secure: Helps ensure secret keys are present and correctly formatted.
  • 🛠️ Easier to Debug: Find configuration problems instantly.

Features - What Can This Assistant Do?

  • 🔍 Checks Your Settings (Validation): Makes sure settings are the right type (text, number, URL, etc.) and follow your rules. Uses the popular Zod library by default, but you can bring your own!
  • 📄 Reads .env Files: Automatically loads settings from .env files – a common way to store them.
  • 🌳 Understands Different "Moods" (Environments): Can load different settings for "development" (.env.development), "production" (.env.production), etc.
  • Handles Multiple Instruction Sheets: You can have a base set of settings and then override them with local ones.
  • 🔗 Smart Links in Settings (Variable Expansion): Lets one setting use the value of another (e.g., FULL_URL = ${BASE_URL}/api).
  • 🤫 Fetches Secret Settings (Asynchronous): Can get super-secret settings from secure vaults before checking everything.
  • 🥇 Knows Who's Boss (Clear Precedence): If a setting is defined in multiple places, schema-env knows which one to use.
  • 🛡️ Doesn't Change Global Settings: It won't mess with your computer's main settings.
  • 🗣️ Clear Error Messages: Tells you all the problems at once, not one by one.
  • 🤖 AI-Powered Helper: This library was built with the help of an AI assistant!

Let's Get Started! (Basic Magic)

1. Install schema-env and zod (our default rulebook maker):

npm install schema-env zod
# or
yarn add schema-env zod

2. Create Your Rulebook (envSchema.ts): Tell schema-env what settings your app needs.

// envSchema.ts
import { z } from "zod"; // Zod helps us make the rules!

export const envSchema = z.object({
  // Rule 1: NODE_ENV should be "development" or "production". Default to "development".
  NODE_ENV: z.enum(["development", "production"]).default("development"),

  // Rule 2: PORT should be a number. If not given, use 3000.
  PORT: z.coerce.number().default(3000),

  // Rule 3: GREETING_MESSAGE must be text, and you *must* provide it!
  GREETING_MESSAGE: z.string().min(1, "Oops! You forgot the greeting message!"),
});

// This creates a TypeScript type for our validated settings - super handy!
export type Env = z.infer<typeof envSchema>;

3. Write Down Your App's Settings (.env file): Create a file named .env in the main folder of your project.

# .env
GREETING_MESSAGE="Hello from schema-env!"
PORT="8080"

(Notice we didn't put NODE_ENV here? Our rulebook says it defaults to "development"!)

4. Tell schema-env to Check Everything (in your app's main file, like index.ts or server.ts):

// index.ts
import { createEnv } from "schema-env";
import { envSchema, Env } from "./envSchema.js"; // Use .js for modern JavaScript modules

let settings: Env; // This will hold our correct settings

try {
  // Time for the magic check!
  settings = createEnv({ schema: envSchema });
  console.log("✅ Hooray! All settings are correct!");
} catch (error) {
  console.error("❌ Oh no! Something's wrong with the settings.");
  // schema-env already printed the detailed error messages for us!
  process.exit(1); // Stop the app, because settings are bad.
}

// Now you can safely use your settings!
console.log(`The app says: ${settings.GREETING_MESSAGE}`);
console.log(`Running in ${settings.NODE_ENV} mode on port ${settings.PORT}.`);

// Go ahead and start your amazing app!
// startMyApp(settings);

If you run this and your .env file is missing GREETING_MESSAGE or PORT is not a number, schema-env will tell you!

Doing More Cool Things!

Different Settings for Different "Moods" (e.g., Development vs. Production)

If you have a setting NODE_ENV (like in our example), schema-env is extra smart:

  • If NODE_ENV=development, it will also try to load settings from a file named .env.development.
  • If NODE_ENV=production, it will look for .env.production.

Settings in these specific files will override settings from the main .env file.

Settings That Depend on Other Settings (Variable Expansion)

Want API_URL to be ${HOSTNAME}/api? Easy! First, tell schema-env you want to do this:

settings = createEnv({
  schema: envSchema, // Your usual rulebook
  expandVariables: true, // Set this to true!
});

Then, in your .env file:

HOSTNAME="http://mycoolsite.com"
API_URL="${HOSTNAME}/v1/data"

schema-env will figure out API_URL should be http://mycoolsite.com/v1/data.

Using Multiple .env Files

Sometimes you want a base set of settings and then some local ones that only you use.

settings = createEnv({
  schema: envSchema,
  dotEnvPath: [".env.defaults", ".env.local"], // Checks .env.defaults, then .env.local
});

Later files in the list override earlier ones. And the "mood" specific file (like .env.development) still gets checked after all of these!

For the Pros: Super Secret Settings & Your Own Rules!

Getting Secrets from a Secure Vault (Async Magic with createEnvAsync)

Some settings, like database passwords, are too secret for .env files. You might keep them in a "secrets manager" (like AWS Secrets Manager, HashiCorp Vault, etc.). schema-env can fetch these before it checks all your rules!

// mySecretFetcher.ts
import type { SecretSourceFunction } from "schema-env";

export const fetchMyDatabasePassword: SecretSourceFunction = async () => {
  console.log("🤫 Asking the secret vault for the DB password...");
  // In real life, you'd use a library here to talk to your secrets manager.
  // We'll pretend it takes a moment:
  await new Promise((resolve) => setTimeout(resolve, 50));
  return {
    DB_PASSWORD: "ultra-secret-password-from-vault",
  };
};

Then, in your app:

// index.ts
import { createEnvAsync } from "schema-env"; // Note: createEnvAsync!
import { envSchema, Env } from "./envSchema.js"; // Your schema needs to expect DB_PASSWORD
import { fetchMyDatabasePassword } from "./mySecretFetcher.js";

async function startAppSafely() {
  let settings: Env;
  try {
    settings = await createEnvAsync({
      // await is important here!
      schema: envSchema,
      secretsSources: [fetchMyDatabasePassword], // Add your secret fetchers here
    });
    console.log("✅ Secrets fetched and all settings are correct!");
    // console.log(`DB Password's first letter: ${settings.DB_PASSWORD[0]}`); // Be careful logging secrets!
  } catch (error) {
    console.error(
      "❌ Oh no! Something went wrong with settings (maybe secrets?)."
    );
    process.exit(1);
  }
  // startMyApp(settings);
}

startAppSafely();

Don't Like Zod? Bring Your Own Rulebook Checker! (Custom Adapters)

If your team already uses another library like Joi or Yup to define rules, you can tell schema-env to use that instead of Zod!

You'll need to create a small "adapter" that teaches schema-env how to talk to your chosen library. This involves implementing the ValidatorAdapter interface provided by schema-env.

To use a custom validation library (like Joi, Yup, or your own):

  1. Define your environment type and schema using your chosen library.
  2. Implement the ValidatorAdapter<TResult> interface from schema-env. This adapter will:
    • Take the merged environment data as input.
    • Use your chosen library to validate this data.
    • Return a ValidationResult<TResult> object, which tells schema-env if validation succeeded (and the typed data) or failed (with standardized error details).
  3. Pass an instance of your adapter to createEnv or createEnvAsync using the validator option. You'll also need to provide the expected result type as a generic argument (e.g., createEnv<undefined, MyCustomEnvType>({ validator: myAdapter })).

For a complete, runnable example showing how to create and use a custom adapter with Joi, please see the examples/custom-adapter-joi/ directory in this repository. It includes: _ A Joi schema definition (env.joi.ts). _ The Joi adapter implementation (joi-adapter.ts). * An example of how to use it (index.ts).

This demonstrates the flexibility of schema-env in integrating with various validation workflows.

Who Wins? The Order of Settings (Precedence)

If a setting is defined in multiple places, here's who wins (highest number wins):

For createEnv (the simpler one):

  1. Default values in your rulebook (schema).
  2. Values from your .env file(s) (and expanded if you turned that on).
  3. Values from your computer's actual environment (these are like global settings).

For createEnvAsync (the one for secrets):

  1. Default values in your rulebook (schema).
  2. Values from your .env file(s) (expanded if on).
  3. Values fetched from your secretsSources (the secret vaults).
  4. Values from your computer's actual environment.

Quick Look at the Main Tools (API Reference)

createEnv(options)

  • Checks settings right away.
  • If something is wrong, it stops and tells you (throws an error).
  • Returns your perfectly validated settings.

async createEnvAsync(options)

  • Can fetch secrets from vaults first.
  • Then checks all settings.
  • If something is wrong, it tells you by rejecting its Promise.
  • If all good, its Promise gives you the validated settings.

Key Options (for both tools):

  • schema: Your Zod rulebook. (Use this OR validator)
  • validator: Your custom rulebook checker. (Use this OR schema)
  • dotEnvPath: Which .env file(s) to read. (e.g., './.env.custom' or ['./.env.base', './.env.local']). Defaults to just ./.env. Can be false to load no .env files.
  • expandVariables: true or false to turn on smart links in .env files. (Defaults to false)
  • secretsSources: (Only for createEnvAsync) A list of functions that go fetch your secrets.

Want to Help or Have Ideas? (Contributing)

That's awesome! We'd love your help.

  • New ideas, bug reports, and improvements are always welcome. Feel free to open an issue or a pull request.

License

MIT © devvictrix (AI Assisted)