惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
有赞技术团队
有赞技术团队
IT之家
IT之家
博客园 - 聂微东
Jina AI
Jina AI
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
小众软件
小众软件
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
Visual Studio Blog
雷峰网
雷峰网
酷 壳 – CoolShell
酷 壳 – CoolShell
阮一峰的网络日志
阮一峰的网络日志
宝玉的分享
宝玉的分享
博客园 - 三生石上(FineUI控件)
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
量子位
月光博客
月光博客
博客园 - 【当耐特】
博客园 - 叶小钗

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
dotenv loads your .env — it doesn't check it. So I built ...
benjamin · 2026-06-19 · via DEV Community
Cover image for dotenv loads your .env — it doesn't check it. So I built a typed validator.

benjamin

A PORT=8O80 typo (that's a letter O), a DATABASE_URL you forgot to set, a NODE_ENV=prodd — none of these fail when your app starts. They fail later: a cryptic stack trace three layers into startup, a service that boots but talks to the wrong database, a feature flag that's silently off in production. The error is never "your env is wrong"; it's whatever broke downstream.

dotenv loads your .env. It doesn't check it. So I built envward: validate the whole environment against a small typed schema up front, and fail loudly — with the actual problem — before a single line of app code runs. Zero dependencies, no network.

$ envward

.env — checked against env.schema.json

  ✗ API_KEY   missing — required string
  ✗ NODE_ENV  "prodd" is not one of: development, production, test
  ✗ PORT      70000 is above max 65535

3 problem(s), 3 key(s) valid

It exits non-zero on any problem, so it drops straight into a prestart hook or a CI step.

Get a schema in one command

No hand-writing JSON from scratch:

envward --init > env.schema.json

--init reads your existing .env and guesses a type for each key (8080int, https://…url, truebool, …), marking them required. Then you tighten it:

{
  "PORT":         { "type": "int", "required": true, "min": 1, "max": 65535 },
  "DATABASE_URL": { "type": "url", "required": true },
  "NODE_ENV":     { "type": "enum", "values": ["development", "production", "test"] },
  "API_KEY":      { "type": "string", "required": true, "minLength": 16 }
}

Types: string (with minLength/maxLength/pattern), int / number (with min/max), bool, url, email, enum. An empty value (KEY=) counts as missing.

How it's different from a drift checker

A .env drift tool tells you which keys are missing versus .env.example. envward validates the values: is PORT actually an integer in range, is DATABASE_URL actually a URL, is NODE_ENV one of the allowed set. Different failure mode, caught at a different time.

Install

npx envward          # Node
pip install envward  # Python — same behavior

Two builds (Node + Python) that validate identically, so it fits whatever your stack already runs.

Use it as a gate

# package.json: "prestart": "envward"   — refuse to boot with a broken .env
# CI:           envward --env .env.ci --strict

--strict also flags keys present in .env but missing from the schema.

A couple of honest notes

  • Zero dependencies, both builds — stdlib only.
  • A malformed schema is an error, not a guess. A non-numeric min, a bad regex pattern, an unknown type — envward exits 2 with a clear message in both builds, rather than crashing or silently passing. (Getting Node and Python to agree on every edge here took a real adversarial pass.)
  • pattern is matched in ASCII mode and as an unanchored search — wrap it in ^…$; keep to a portable regex subset for identical behavior across both builds.

Links


How do you guard environment config today — a hand-rolled startup check, a framework feature, or just hope? And would you gate CI on it?