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

推荐订阅源

N
News and Events Feed by Topic
Malwarebytes
Malwarebytes
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
F
Future of Privacy Forum
C
Cisco Blogs
T
The Exploit Database - CXSecurity.com
A
Arctic Wolf
S
Securelist
K
Kaspersky official blog
S
Schneier on Security
T
ThreatConnect
T
Tenable Blog
Spread Privacy
Spread Privacy
T
True Tiger Recordings
AWS News Blog
AWS News Blog
F
Fox-IT International blog
量子位
T
Threatpost
V
Vulnerabilities – Threatpost
C
CERT Recently Published Vulnerability Notes
Cisco Talos Blog
Cisco Talos Blog
GbyAI
GbyAI
宝玉的分享
宝玉的分享
腾讯CDC
G
Google Developers Blog
aimingoo的专栏
aimingoo的专栏
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Visual Studio Blog
U
Unit 42
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
The Register - Security
The Register - Security
MyScale Blog
MyScale Blog
小众软件
小众软件
A
About on SuperTechFans
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
博客园 - 三生石上(FineUI控件)
美团技术团队
Google Online Security Blog
Google Online Security Blog
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog

DEV Community

Building an Autonomous SRE Agent: From Raw Telemetry to Safe, AI-Driven Remediation The EU AI Act in 2026: Reading the Law After the Omnibus I had zero coding knowledge. Here is "RetroTube", a 2010 YouTube sandbox prototype I built using AI! I Built a CLI Tool That Writes Better Git Commits Than I Do Transfer Fees, Metadata, and Soulbound Tokens: My First Real Token Experiments on Solana Stop Using Fetch() in React: A Better Way To Call Your Backend Creando un Tetris con JavaScript VI: Complicando el juego. DeepSeek's API Price Cut Changed My Claude Code and ChatGPT Math [Boost] Perl 🐪 Weekly #774 - Perl is too HOT How to Track AI Usage Without Losing Revenue (Complete Guide) 77 Rules Later: What Graduating Our First Stack Actually Looked Like RAG 시스템 실전 구축 (v26) When Premature Scaling Leads to Operator Burnout Multi-Repo Microservice Changes Are a Coordination Problem. I Solved It With AI Agent Teams. The Next Frontier: How Multi-Agent Systems are Redefining Productivity The Kimwolf Bust Just Outed Android Webcams as Botnet Fodder — Here's the Question Every Repurposed-Phone Camera Setup Has to Answer I'm an autonomous AI agent. I shipped 18 fixes to myself in one session. Building a Secure Future with Zero Trust Security Architecture Asynchronous Functions in Dart How I migrated magic-link login from Resend to AWS SES + Lambda five days before launch Edge Computing He creado una empresa ficticia IT/OT para poder encontrar sus vulnerabilidades y reforzar su seguridad en sus activos críticos Why I Built @editora/react I built a tiny UGC script generator because hooks are the hardest part The Phone Is Becoming the New Terminal Why Most AI Music Tools Feel Wrong to Developers Goroutines vs. Promises: Why Go and JavaScript Look at Concurrency Completely Differently How I Use Antigravity 2.0 to Navigate Open-Source Codebases and Make Better Technical Decisions Understanding Basic HTML & CSS Concepts for Beginners Go Error Handling: Annoying or Awesome? Your To-Do List Doesn't Know You — So I Gave Mine Three Brains Shell Basics (Bash, Zsh, Sh) Free MongoDB GUI Tool for Developers, Students, and Teams Designing High-Performance Blockchain Indexers Choosing Models for an Agentic Chat App on Amazon Bedrock How Smart Growth Teams Automate Their Marketing Stack in 2026 (Without Hiring More People) What I Learned About Memory-Augmented AI Agents Seven Docker Tips Every Engineer Should Know (from Docker Captains) Welcome to the Fast-Food Era of Testing: Over-Weight by Tests How to use Claude in vscode? Prompt Engineering for Automated Evaluation: Making LLMs the Judge in AI Builder Solutions Full Stack Projects Are Not Enough Anymore Virtualization & Cloud Basics Orakle: Turning Raw Blockchain Data into Intelligence with Gemma 4 Building an Autoposting Pipeline with Hermes Agent: Why Waterfall Beats Parallel, and the Edge Cases Nobody Talks About OpenShift Virtualization Migration Advisor — Local-First, Powered by Gemma 4 26B MoE WebMCP is coming — so I’m building webmcp.js I Disappeared for 4 Months After Launch - Here's What Brought Me Back Jira Is Turing-Complete (And You've Been Coding in It) NyayAI: Building an AI Legal Assistant for 1.4 Billion People — A Technical Deep Dive E-commerce Order Automation: Stripe + Invoice + Shipping Workflow How to Evaluate AI Agents: LLM-as-Judge Tutorial The Interview Prep Stack I Used as a Senior Software Engineer Targeting Big Tech Gemma4 Challenge OptiLearn - Powered by Google Gemma 4 Aura — The Gemma 4 Powered Agentic Web Copilot & Self-Healing Accessibility Engine I built a tool that catches misleading charts using Gemma 4 running locally Worklog companion with Gemma4 GBase: Building LLM Agents That Actually Learn from Their Mistakes Blossom — a small step toward student mental wellbeing WordPress Performance Monitoring: A Complete Guide Principal Components in TypeScript (Part 4) When three sharp wallets agree: what consensus signals on Polymarket actually mean I Built a Fail-Fast Rust Scheduler with Background OAuth Auto-Refresh (Part 2) Sharing is caring How Putting Faces (Literally) to My AI Garden Images Gave It a Personality Sofi Log #001: Thailand's Tourism Tax & the 180-Day AI Surveillance Wall Sofi Log #006: Decentralized IP-Address Obfuscation Specs Sofi Log #008: Bypassing Legacy Cross-Border Bank Fee Traps Secret Rotation Automation: The Operational Cost of Security Sofi Log #009: Portable Identity & DID Passport Framework Sofi Log #011: Autonomous Smart Treasury Repatriation Specs History of Linux & Unix I asked Claude if my plan was on track for the goal — and got an honest 'No' PHPStan 'expects X, Y given' — the trace it doesn't give you Using Gemma4 2B to Assist Community Health Workers Open-source Playwright wrapper that passes bot.sannysoft.com, pixelscan, and CreepJS in headless mode Policy Storyteller: Turning Nepali Bills into Human Stories with Gemma 4 Avoid Cross Module Dependencies with Dependency Cruiser Invariant-Driven Architecture: 20M transactions on a €80/mo Cloud VM. Stop using external npm packages just to generate a UUID v4 Choosing the Right Gemma 4 Model Matters More Than Choosing the Best One Your LLM Is Not an Agent. Your Framework Is Not Enough. You Need a Harness. From HTTPS to UCP: Shopping Is About to Stop Being Your Problem From Creation to Consumption: How Antigravity 2.0 and Gemini Spark Are Defining the Agentic Era 10 Mistakes I Wish I Knew Before Taking the CKA Exam AI That Actually Does Stuff: Autonomous Agents Explained Exploring AI workflow Orchestration: Comparing Weft, Python & Alternative Pipeline Approaches El Poder del Aprendizaje Federado: Cuando los Algoritmos Distribuidos Entrenan a la IA Email Marketing Automation in 2026: 5 Tools (and 1 Self-Hosted) Through Their APIs A Replay Runbook For Missed Publishing Windows Why timeout handling matters more than most backend logic How I Make $6,800/Month Selling Niche VS Code Extensions Model Routing Cost Checklist: Hosted APIs, Open Models, Or Self-Hosted Inference? ORA-00207 오류 원인과 해결 방법 완벽 가이드 Deno 2.8 Operator Upgrade Checklist: CI, Lockfiles, Node Compatibility, And Rollback AI-Discovered Vulnerabilities Need A Triage Queue, Not A Panic Channel AI Agent Workboards Need Audit Controls Before They Need More Agents Demystifying DevRel: What It Actually Is (And Why Should You Become One?)
How to Validate Environment Variables in TypeScript (and Why You Should)
AW · 2026-05-25 · via DEV Community

Every developer has a story about a .env file causing a production outage. Maybe it was a missing DATABASE_URL that silently defaulted to undefined. Maybe NODE_ENV was set to staging instead of production, and staging API keys leaked into production traffic. Or perhaps a port number was accidentally typed as a string, and the server crashed with a cryptic type error.

Environment variables are the most common way to configure applications, but they have no built-in safety net. A typo, a missing value, or a misconfigured variable can reach production without a single warning — until your monitoring dashboard turns red.

In this tutorial, you'll learn how to define a schema for your environment variables, validate them automatically, generate TypeScript types from your schema, and catch configuration errors before they reach production.

The Problem: .env Files Have No Guardrails

Consider a typical .env file:

PORT=3000
DATABASE_URL=postgresql://localhost:5432/myapp
NODE_ENV=development
API_KEY=

Enter fullscreen mode Exit fullscreen mode

Now consider what happens when:

  • PORT accidentally gets set to "abc" — your server fails to bind
  • NODE_ENV is set to "staging" — your production environment uses staging credentials
  • API_KEY is blank — third-party API calls fail with 401s
  • DATABASE_URL uses http:// instead of postgresql:// — the connection pool silently fails

Without validation, each of these scenarios causes a runtime failure. With validation, they're caught in CI before deployment.

Introducing Schema-Based Validation

The fix is simple: define what each variable should look like, then check your .env file against that schema before anything runs.

A schema for the variables above might look like:

{
  "vars": {
    "PORT": {
      "type": "number",
      "required": true,
      "format": "port",
      "default": 3000
    },
    "DATABASE_URL": {
      "type": "string",
      "required": true,
      "format": "url"
    },
    "NODE_ENV": {
      "type": "string",
      "enum": ["development", "production", "test"],
      "default": "development"
    },
    "API_KEY": {
      "type": "string",
      "required": true
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

This schema declares:

  • PORT must be a number, must be a valid port (1–65535), and defaults to 3000
  • DATABASE_URL must be a string, must be a valid URL, and is required
  • NODE_ENV can only be one of three values and defaults to development
  • API_KEY must be a string and is required

Validating Your .env File

The tool we'll use is env-haven, a zero-dependency CLI that validates .env files against a JSON schema. Install it with a single command:

npx env-haven

Enter fullscreen mode Exit fullscreen mode

Save the schema above as checkmyenv.config.json in your project root. Then run:

env-haven

Enter fullscreen mode Exit fullscreen mode

If all variables are valid, you'll see:

env-haven — Environment Variable Validation

  ✓ PORT = 3000
  ✓ DATABASE_URL = postgresql://localhost:5432/myapp
  ✓ NODE_ENV = development
  ✓ API_KEY = sk-abc123

PASS  4 vars — 4 passed, 0 failed

Enter fullscreen mode Exit fullscreen mode

If something is wrong, you get clear error messages:

env-haven — Environment Variable Validation

  ✗ PORT = abc
    │ "PORT" must be a number (got "abc")
    │ "PORT" must be a valid port (1-65535, got "abc")
  ✗ NODE_ENV = staging
    │ "NODE_ENV" must be one of: development, production, test (got "staging")
  ✗ API_KEY = (not set)
    │ Missing required variable "API_KEY"

FAIL  4 vars — 1 passed, 3 failed

Enter fullscreen mode Exit fullscreen mode

The exit code is 1 on failure, which means you can plug this into any CI pipeline:

# .github/workflows/validate-env.yml
name: Validate environment config
on: [pull_request]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx env-haven

Enter fullscreen mode Exit fullscreen mode

This checks the default .env file. For staging or production, you can validate against different .env files by copying them into place before running the check.

Going Further: Generators

Typing out your schema is useful, but env-haven can do more.

Generate a .env.example

Keep your .env.example in sync with your schema automatically:

env-haven generate

Enter fullscreen mode Exit fullscreen mode

This produces:

env-haven: Generated .env.example

Enter fullscreen mode Exit fullscreen mode

The output file has every variable listed with its default value and a comment explaining what it's for. Required variables are marked explicitly:

# Server port
# PORT=3000
# ^^^ REQUIRED: uncomment and set this value

# PostgreSQL connection string
DATABASE_URL=

# Environment name
# NODE_ENV=development

# API authentication key
API_KEY=

Enter fullscreen mode Exit fullscreen mode

Required variables are uncommented so they fail loudly if unset. Optional ones are commented out with their defaults filled in. This makes onboarding new team members trivial — they can copy .env.example to .env, uncomment the variables they need, and go.

Generate TypeScript Types

If you access environment variables through process.env, you've probably written something like this:

const port = parseInt(process.env.PORT || "3000", 10);

Enter fullscreen mode Exit fullscreen mode

This works, but it's verbose, error-prone, and doesn't scale. A better approach is to define a typed interface for your environment. env-haven can generate it for you:

env-haven types

Enter fullscreen mode Exit fullscreen mode

This creates an env.d.ts file:

// Auto-generated by env-haven

export interface Env {
  readonly PORT: number;
  readonly DATABASE_URL: string;
  readonly NODE_ENV: string;
  readonly API_KEY: string;
}

Enter fullscreen mode Exit fullscreen mode

Now you can use it in your application:

import type { Env } from "./env";

function getEnv(): Env {
  return {
    PORT: parseInt(process.env.PORT!, 10),
    DATABASE_URL: process.env.DATABASE_URL!,
    NODE_ENV: process.env.NODE_ENV!,
    API_KEY: process.env.API_KEY!,
  };
}

Enter fullscreen mode Exit fullscreen mode

Pair this with a validation step in your build, and you get compile-time confidence that your environment is correctly configured.

Schema Reference

Here's every validation rule available:

Rule Example What it checks
type "number", "boolean", "integer" Value has the correct JavaScript type
required true / false Value is present (unless a default is set)
default 3000 Fallback value when the variable is not set
format "url", "email", "port" Value matches an expected format
enum ["dev", "prod"] Value is in the allow-list
pattern "^sk-" Value matches a regular expression
min 1 Minimum length (strings) or value (numbers)
max 65535 Maximum length (strings) or value (numbers)

Supported formats include: url, email, port, uuid, hostname, path, and regexp.

Integrating With Your Workflow

The most effective setup is three steps:

  1. Commit your schemacheckmyenv.config.json lives in version control alongside your code
  2. Validate in CI — run npx env-haven as a lint step in every pull request
  3. Generate on change — run env-haven generate whenever the schema changes, and commit the updated .env.example

This creates a virtuous cycle: the schema is the source of truth, the .env.example is always accurate, and bad configuration never reaches production.

Conclusion

Environment variable validation is one of those small investments that pays for itself the first time it catches a bug. A schema takes five minutes to write, but it prevents the kind of production incidents that take hours to debug.

The tool we used, env-haven, is open source (MIT), has zero dependencies, and runs in under 100ms. Try it on your next project:

npx env-haven

Enter fullscreen mode Exit fullscreen mode

Your future self — and your on-call rotation — will thank you.