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

推荐订阅源

WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
月光博客
月光博客
博客园 - Franky
Martin Fowler
Martin Fowler
U
Unit 42
阮一峰的网络日志
阮一峰的网络日志
Recent Announcements
Recent Announcements
The Cloudflare Blog
博客园 - 聂微东
酷 壳 – CoolShell
酷 壳 – CoolShell
宝玉的分享
宝玉的分享
J
Java Code Geeks
B
Blog RSS Feed
博客园 - 三生石上(FineUI控件)
MongoDB | Blog
MongoDB | Blog
腾讯CDC
博客园_首页
博客园 - 司徒正美
D
DataBreaches.Net
I
InfoQ
GbyAI
GbyAI
IT之家
IT之家
罗磊的独立博客

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
Framework-Specific Env Patterns
Odejobi Abiola Samuel · 2026-06-27 · via DEV Community
Cover image for Framework-Specific Env Patterns

Odejobi Abiola Samuel

Your schema is portable. But each runtime loads environment variables differently. CtroEnv adapters bridge the gap — same validation logic, different data sources.

Node.js: process.env + .env Files

The @ctroenv/node adapter loads .env files and wraps process.env:

import { defineEnv, string, number } from "@ctroenv/core"
import { loadEnv } from "@ctroenv/node"

const env = defineEnv(schema, { source: loadEnv() })

loadEnv() resolves files in order:

  1. .env — shared defaults
  2. .env.{NODE_ENV} — environment-specific (.env.development, .env.production)
  3. .env.local — local overrides (gitignored)

Later files override earlier ones. process.env takes precedence unless override: true.

Monorepo Root

loadEnv({ path: "../.." }) // look up two directories for root .env

Native Node 22+

Node 22 has built-in process.loadEnvFile(). Use native: true to delegate:

loadEnv({ native: true }) // uses process.loadEnvFile() if available

Falls back to the custom parser on older Node versions.

System Fallback

By default, only file values are returned. With system: true, missing keys fall through to process.env:

loadEnv({ system: true })

Standalone Parser

Use parseEnvFile() directly for custom file loading:

import { parseEnvFile } from "@ctroenv/node"

const content = readFileSync(".env.custom", "utf-8")
const vars = parseEnvFile(content)

Handles quotes, multiline values (backslash continuation), interpolation (${VAR}), comments, and export prefix.

Vite: Build-Time Validation

The @ctroenv/vite plugin validates during the build:

// vite.config.ts
import { ctroenvPlugin } from "@ctroenv/vite"

export default defineConfig({
  plugins: [
    ctroenvPlugin({ schema: "./src/env.ts" }),
  ],
})

If DATABASE_URL is missing, the build fails — no broken artifacts shipped.

Schema Options

Pass a file path or inline definition:

// File path — imports the module, looks for `schema` export
ctroenvPlugin({ schema: "./src/env.ts" })

// Inline definition
ctroenvPlugin({
  schema: {
    DATABASE_URL: string().url(),
    PORT: number().port().default(3000),
  },
})

Fail on Error

ctroenvPlugin({ schema: "./src/env.ts", failOnError: false })
// warns instead of failing — useful for optional env vars

viteSource()

Use with defineEnv() directly in Vite code:

import { defineEnv } from "@ctroenv/core"
import { viteSource } from "@ctroenv/vite"

const env = defineEnv(schema, { source: viteSource() })

viteSource() reads from import.meta.env first, then falls back to process.env.

Next.js: Server/Client Split

Next.js bundles code for the browser. Server-only env vars must never reach the client bundle. The @ctroenv/nextjs adapter enforces this at runtime:

import { string, type ClientServerSchema } from "@ctroenv/core"
import { defineEnv } from "@ctroenv/nextjs"

const schema = {
  server: {
    DATABASE_URL: string().url(),
    JWT_SECRET: string().min(32).secret(),
  },
  client: {
    NEXT_PUBLIC_API_URL: string().url(),
  },
} satisfies ClientServerSchema

const env = defineEnv(schema)

Server components access everything. Client components can only access NEXT_PUBLIC_ variables — accessing a server var throws:

Server-only environment variable "DATABASE_URL" is not accessible on the client.
Prefix it with NEXT_PUBLIC_ to expose it.

Build-Time Validation

Wrap your Next.js config:

// next.config.ts
import { withCtroEnv } from "@ctroenv/nextjs"

export default withCtroEnv(schema, nextConfig)

Validates at config load time — before the build starts.

Accessing Secrets

Server secrets are masked ("********"). Use meta.get() for raw values:

env.JWT_SECRET           // "********"
env.meta.get("JWT_SECRET") // actual value

Choosing an Adapter

Runtime Adapter Source Best for
Node.js @ctroenv/node .env files + process.env APIs, CLIs, servers
Vite @ctroenv/vite import.meta.env Frontend apps, SSG
Next.js @ctroenv/nextjs Server/client split Full-stack apps
Cloudflare Workers core's workersSource() Worker env binding Edge functions
Deno/Bun core's detectSource() Auto-detected Cross-runtime apps

All adapters use the same schema. Switch between them by changing the source.

npm install @ctroenv/node @ctroenv/vite @ctroenv/nextjs

Links: GitHub · Docs · npm

Previous: Type-Safe Env Vars Without Zod
Next: Testing and Debugging Your Env Config