Projects / Open Source Package

Open Source Package

env-guard

Every deploy has the same failure mode. You push, the build goes green, and then the app dies at 2am because `DATABASE_URL` was never set on the new box.

Every deploy has the same failure mode. You push, the build goes green, and then the app dies at 2am because DATABASE_URL was never set on the new box. Not a bug in your code. A bug in the absence of your code: a variable nobody checked for until the moment it mattered.

env-guard is my answer to that specific failure. It reads a .env file into process.env, then checks it against a schema you define: which keys are required, what type each one should be (string, number, boolean, url, email, port), and it throws one clean error listing everything that's wrong instead of letting your app limp along with undefined where a database URL should be. That's it. That's the whole pitch. Not a framework. Not a platform. A guard rail you bolt on before the rest of the app starts.

I built it because I got tired of writing the same six lines of validation at the top of every project. Check if PORT exists. Check if it's a number. Check if API_KEY is set. Repeat, slightly differently, in every codebase, forever. Zod plus dotenv solves this, technically, but it's two dependencies and a fair amount of boilerplate for something that should be one function call. env-guard is the version of that idea with the boilerplate surgically removed.

What's actually in it

The whole thing lives in one file, src/index.ts, a hundred and sixty-one lines. No dependencies at runtime, because I didn't want a schema validator to become a liability in its own supply chain. It parses .env itself rather than pulling in dotenv, which meant writing a small parser that strips quotes and skips comments. Nothing clever. Just enough to work.

The core function is guard(). You hand it a schema object (a map of field names to types, with an optional required flag and default values) and it returns a typed object with everything coerced and validated. There's a generic, InferType<S>, that lets TypeScript infer the shape of your config object directly from the schema, so you get autocomplete on config.databaseUrl without writing a separate interface. That part I'm genuinely proud of. It's a small trick, but it means the schema is the single source of truth instead of the schema and a type living in two places and drifting apart.

There's also a failFast option. By default the guard collects every error it finds and throws them all at once, so you fix your .env in one pass instead of playing whack-a-mole with sequential crashes. Turn failFast on and it stops at the first bad field instead. Small option, but it's the difference between a five-minute setup and a twenty-minute one when you're the person who forgot to set four variables at once.

The honest part

Here's the thing I'm not going to hide: the README has a bug in it. The usage example calls guardEnv(). The actual exported function is guard(). I wrote the docs and the code in the same sitting and somehow typed the function name differently in two places, and nobody, meaning me, because I'm the only user, caught it before it shipped. It's a small thing. It's also exactly the kind of small thing that separates a real, lived-in tool from a polished demo. Demos don't have typos in the README because demos get read once, by the person selling them. Tools that get used have rough edges, because using something surfaces the edges that reading it never does.

There's no test suite. No examples folder. No CHANGELOG. I know how that reads. But this isn't a library I'm asking a thousand people to trust with their production config. It's a library I built because I needed exactly this, twelve times, across twelve projects, and writing it once was faster than writing it twelve times slightly worse each time. The test suite for env-guard, in the most literal sense, is every project of mine that imports it and either builds or doesn't.

Where it sits in the stack

I ship a handful of these small, single-purpose packages, port-killer, is-wsl2, esbuild-plugin-env, and the rest of them, and env-guard is the one I reach for first, because config is the first thing that breaks and the last thing anyone thinks to guard. Nexli has a .env with a dozen keys across Firebase, SQL, and auth. Getting even one of them wrong silently is worse than getting it wrong loudly, because silent failure shows up as a support ticket three weeks later instead of a red build today. env-guard exists to move the failure earlier, where it's cheap, instead of later, where it's a school administrator emailing me asking why the login page is blank.

I didn't build this to sell it. I built it because after the third project I was copy-pasting the same validation function and thought: this should just exist, once, somewhere I control. Publishing it to npm was almost an afterthought; the actual motivation was selfish. I wanted npm install env-guard to be faster than writing the function again. It is.

What I'd change

If I sat down with it again, I'd fix the README typo first. That one's just embarrassing. Then I'd add a real test file, not because I think the logic is wrong, but because a schema validator without tests is asking to be trusted on vibes, and vibes aren't a contract. I'd also probably split the .env parser out so esbuild-plugin-env and env-guard share it instead of each carrying its own copy, which is currently the case. I wrote the same twenty-line parser twice, a few weeks apart, because I'd forgotten I already had one. That's the kind of thing that happens when you're building fast and alone: you solve the same small problem more than once because pausing to check "have I done this before" costs more time than just doing it again.

None of that changes what the tool does today. It reads your .env, checks it against a schema, and refuses to let your app start with silently missing config. Small claim, kept.

Full stack developer. Founder of Yashveer Labs. This is one of the small tools that keeps the bigger ones honest.

Start a conversation about this.

Whether it's env-guard itself or the next system worth building, the lab is reachable.