Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosGoogle Cloud Tech: Vibe coding in the pit lane 🏁(23.09.2026 um 01:00 Uhr)
Sichere ProgrammierungBuild an Explainable Vendor-Risk Gate in Node.js(23.09.2026 um 00:27 Uhr)
Sichere ProgrammierungFrom p=none to Enforcement: A Working Sequence for DMARC Rollout(23.09.2026 um 00:40 Uhr)
Sichere ProgrammierungWhen OPA's Bundle Loader Runs Past a `.manifest` Typo(23.09.2026 um 00:53 Uhr)
Sichere ProgrammierungGovernance Attack Surface Review: Bybit(23.09.2026 um 01:00 Uhr)
Linux Tipps & HardeningOpenShot video editor is now available as a snap(23.09.2026 um 00:09 Uhr)
KI & AI VideosAI Revolution: AI Robots Are Beating Humans Now(23.09.2026 um 00:32 Uhr)
YouTube Security VideosGoogle Cloud Tech: Vibe coding in the pit lane 🏁(23.09.2026 um 01:00 Uhr)
Sichere ProgrammierungBuild an Explainable Vendor-Risk Gate in Node.js(23.09.2026 um 00:27 Uhr)
Sichere ProgrammierungFrom p=none to Enforcement: A Working Sequence for DMARC Rollout(23.09.2026 um 00:40 Uhr)
Sichere ProgrammierungWhen OPA's Bundle Loader Runs Past a `.manifest` Typo(23.09.2026 um 00:53 Uhr)
Sichere ProgrammierungGovernance Attack Surface Review: Bybit(23.09.2026 um 01:00 Uhr)
Linux Tipps & HardeningOpenShot video editor is now available as a snap(23.09.2026 um 00:09 Uhr)
KI & AI VideosAI Revolution: AI Robots Are Beating Humans Now(23.09.2026 um 00:32 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Environment Variables Don't Have to Suck

I've lost count of how many times I've seen this in production: const port = parseInt(process.env.PORT || "3000", 10) const dbUrl = process.env.DATABASE_URL if (!dbUrl) throw new Error("DATABASE_URL is required") It's in every…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

I've lost count of how many times I've seen this in production:




const port = parseInt(process.env.PORT || "3000", 10)
const dbUrl = process.env.DATABASE_URL
if (!dbUrl) throw new Error("DATABASE_URL is required")






It's in every codebase. The same six lines, copy-pasted across a dozen files. Half the time someone forgets parseInt exists and the port becomes the string "3000". The other half the error message is misleading because someone renamed the variable in the schema but forgot to update the check.



I got tired of fixing these bugs and built something small.






The core idea



One function. A typed schema. Zero dependencies.




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

const env = defineEnv({
DATABASE_URL: string().url(),
PORT: number().port().default(3000),
})

// env.DATABASE_URL is `string`
// env.PORT is `number` — coerced from the env var string






That's it. You describe what you expect, and you get a typed, frozen object back. If the actual environment doesn't match, you get a clear error instead of a cryptic undefined downstream.






What it actually does at runtime



When you call defineEnv(), it:




  1. Reads each variable from process.env (or any source you give it)

  2. Runs it through the validator

  3. Applies defaults for missing optional vars

  4. Freezes the result object



No hidden file reads. No global state. No runtime dependencies.






Where this saves real time






1. No more startup crashes with bad types






const env = defineEnv({
REDIS_PORT: number().port(),
})
// If REDIS_PORT is "abc" or "99999", this throws immediately
// with: "REDIS_PORT: Expected a port number (1-65535), received 99999"






Instead of your app starting, connecting to Redis, and then failing with some obscure connection error 30 seconds later, you get the failure at import time.






2. Boolean coercion that actually makes sense






const env = defineEnv({
DEBUG: boolean(),
})
// Coerces common boolean strings: "true", "1", "yes", "on" → true






No more comparing strings to "true" manually.






3. Secrets that don't leak in logs






const env = defineEnv({
JWT_SECRET: string().min(32).secret(),
})

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






The Proxy wrapper masks secret values on direct access. You have to explicitly use .meta.get() to read them. This means a stray console.log(env) in a request handler won't dump your secrets.






The CLI is worth mentioning



I use this more than the library itself some days:




ctroenv validate          # Check env against your schema
ctroenv check --strict # CI gate — exits non-zero on mismatch
ctroenv generate # Create .env.example from schema






The check command is what runs in CI. It diffs the .env file against the schema and exits 1 if anything's missing or wrong. The --warn-unknown flag also catches typos in your .env file (like DATABASE_URLL with a suggestion for DATABASE_URL).






How it handles real-world mess






Next.js (server/client split)






import { defineEnv } from "@ctroenv/nextjs"

export const env = defineEnv({
server: {
DATABASE_URL: string().url(),
JWT_SECRET: string().secret(),
},
client: {
NEXT_PUBLIC_API_URL: string().url(),
},
})






Server-only variables throw if accessed on the client. The types are merged so you get one typed object.






Vite plugin






import { ctroenvPlugin } from "@ctroenv/vite"

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






Validates env at build time. If you forgot to set VITE_API_URL, the build fails before you deploy.






Watching for changes






import { watchEnv } from "@ctroenv/core"

const env = watchEnv(schema, {
pollInterval: 200,
onChange(key, oldVal, newVal) {
console.log(`${key} changed: ${oldVal}${newVal}`)
},
})






Useful for local dev servers that need to react to .env file changes without a restart.






What I left out



I deliberately kept validators minimal. No hex() or base64(). The philosophy is: ship the 80% case, and let people write custom validators for the rest.




import { createValidator, applyChain, parseOk, singleError, errType, errInvalid } from "@ctroenv/core"

function hexColor() {
const base = createValidator<string>(
(input, ctx) => {
if (typeof input !== "string")
return singleError(errType(ctx.key, typeof input, "hex color"))
if (!/^#[0-9a-fA-F]{3,6}$/.test(input))
return singleError(errInvalid(ctx.key, input, "not a valid hex color"))
return parseOk(input)
},
{ typeLabel: "hexcolor" },
)
return applyChain(base)
}






Sixteen lines. No base class. No decorator magic.






The numbers




  • Core package: zero dependencies, ~4 KB gzipped

  • 480 tests, 98% line coverage

  • Works in Node 18+, Bun, Deno, Cloudflare Workers, Vite, Next.js



You can find it at github.com/ctrotech-tutor/ctroenv.






I wrote this library because I was tired of debugging "cannot read properties of undefined" that traced back to a missing env var. If you've been there too, give it a try — or don't. Either way, stop writing manual process.env checks.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Environment Variables Don't Have to Suck

Thematisch verwandte Begriffe: Environment, Variables, Dont, Have · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-58268 | SIPGO is a library for writing SIP services in the GO language. Prior to…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick