Bootcamp backendontwikkeling met Node.js · Les

Typeveilige omgevingsconfiguratie en runtimevalidatie

Valideer omgevingsvariabelen en externe invoer tijdens runtime met schemabibliotheken die statische typen afleiden.

Les 3 van 413 stappen

Typeveilige omgevingsconfiguratie en runtimevalidatie is een gratis Bootcamp backendontwikkeling met Node.js-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Bootcamp backendontwikkeling met Node.js. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Bootcamp backendontwikkeling met Node.js bevat in totaal 4 lessen.

Waarom process.env valideren?

In Node.js is elke waarde op process.env een tekenreeks of undefined — de runtime geeft je geen garanties.

  • process.env.PORT kan "3000", "" of helemaal niet aanwezig zijn.
  • Een typefout zoals DATABSE_URL levert stilletjes undefined op.
  • TypeScript typeert process.env als Record<string, string | undefined> en kan ontbrekende sleutels daarom niet herkennen.

Als je configuratie overal in je app afzonderlijk uitleest, treden crashes diep in aanvraagafhandelaars op — lang na het opstarten. De oplossing: valideer eenmalig bij het opstarten en stop meteen met een duidelijke melding.

// Untyped, unsafe access scattered everywhere
const port = process.env.PORT;            // string | undefined
const dbUrl = process.env.DATABASE_URL;   // string | undefined

console.log(typeof port);  // "string" or "undefined"
console.log(Number(process.env.MISSING)); // NaN — silent failure

Schemabibliotheken en typeafleiding

Met een schemabibliotheek beschrijf je de vorm en beperkingen van gegevens één keer. Vervolgens kun je tijdens runtime valideren én een statisch TypeScript-type afleiden uit dezelfde definitie.

  • Populaire keuzes zijn: Zod, Valibot, ArkType en TypeBox.
  • Eén bron van waarheid — het schema — levert zowel de runtimecontrole als het type tijdens het compileren.
  • Geen dubbele interface die uit de pas kan gaan lopen.

We gebruiken Zod, de meest voorkomende keuze in het Node.js-ecosysteem. z.infer<typeof schema> haalt het type op dat het schema valideert.

import { z } from "zod";

const UserSchema = z.object({
  id: z.number().int(),
  email: z.string().email(),
});

// Static type inferred from the runtime schema
type User = z.infer<typeof UserSchema>;
// type User = { id: number; email: string }

Een eerste omgevingsschema

Laten we de omgeving beschrijven die onze service nodig heeft. Omdat waarden in process.env altijd tekenreeksen zijn, moet het schema numerieke velden coördineren en tekenreeksvelden beperken.

  • z.coerce.number() zet "3000" om in 3000.
  • z.enum([...]) beperkt een waarde tot een vaste verzameling.
  • Door methoden zoals .min() en .url() aan elkaar te koppelen, voeg je runtimebeperkingen toe.

Definieer het schema in een eigen module, bijvoorbeeld src/env.ts, zodat het één keer bij het opstarten wordt geïmporteerd.

import { z } from "zod";

export const EnvSchema = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]),
  PORT: z.coerce.number().int().positive().default(3000),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
});

Parseren bij het opstarten

Roep schema.parse(process.env) één keer aan, bovenaan je invoerbestand. Als de validatie mislukt, werpt Zod een ZodError en wordt het proces beëindigd voordat er verkeer wordt verwerkt.

  • parse() retourneert een volledig getypeerd en gevalideerd object.
  • Geconverteerde en standaardwaarden zijn al toegepast.
  • Exporteer het resultaat, zodat de rest van de app een getypeerd env-object importeert in plaats van process.env rechtstreeks te gebruiken.
import { z } from "zod";
import { EnvSchema } from "./env-schema";

export const env = EnvSchema.parse(process.env);
// env.PORT is number, env.NODE_ENV is a narrowed union

const server = createServer();
server.listen(env.PORT, () => {
  console.log(`Listening on ${env.PORT} in ${env.NODE_ENV}`);
});

safeParse voor duidelijke fouten

Een onbewerkte stacktrace van ZodError is onoverzichtelijk. Gebruik safeParse om een resultaatobject te krijgen dat je kunt omzetten in een leesbare opstartmelding en beëindig het proces daarna bewust.

  • safeParse retourneert { success: true, data } of { success: false, error } — de functie werpt nooit een fout.
  • error.flatten().fieldErrors groepeert meldingen per veld.
  • Beëindig met process.exit(1), zodat orkestrators zoals Docker, PM2 en k8s een mislukte opstart herkennen.
import { z } from "zod";

const EnvSchema = z.object({
  PORT: z.coerce.number().int().positive(),
  DATABASE_URL: z.string().url(),
});

const parsed = EnvSchema.safeParse(process.env);
if (!parsed.success) {
  console.error("Invalid environment variables:");
  console.error(parsed.error.flatten().fieldErrors);
  process.exit(1);
}
export const env = parsed.data;

Een zelfstandig uitvoerbaar voorbeeld

Hier zie je een zelfstandige demonstratie van het patroon valideren-en-afleiden. We gebruiken een gewoon object in plaats van process.env, zodat een online beoordelaar het zonder installatie kan uitvoeren.

Het voorbeeld toont conversie, een standaardwaarde, een enum en een vriendelijke foutafhandeling — dezelfde technieken die je zou toepassen bij het laden van echte omgevingsvariabelen.

import { z } from "zod";

const Schema = z.object({
  NODE_ENV: z.enum(["development", "production"]).default("development"),
  PORT: z.coerce.number().int().positive(),
});

function loadConfig(raw) {
  const result = Schema.safeParse(raw);
  if (!result.success) {
    throw new Error(JSON.stringify(result.error.flatten().fieldErrors));
  }
  return result.data;
}

const config = loadConfig({ PORT: "8080" });
console.log(config); // { NODE_ENV: 'development', PORT: 8080 }
console.log(typeof config.PORT); // number

.env-bestanden laden met ESM

Lokaal bewaar je configuratie in een .env-bestand. De bibliotheek dotenv leest dit in naar process.env. Laad het bij ESM en TypeScript vóór elke module die configuratie uitleest.

  • Moderne Node (v20.6+) heeft een ingebouwde vlag --env-file=.env — er is geen afhankelijkheid nodig.
  • Als je dotenv gebruikt, roep je dotenv/config helemaal bovenaan aan, omdat ESM-imports vooraf worden verwerkt en geëvalueerd.
  • Commit nooit echte geheimen; commit wel een .env.example waarin de vereiste sleutels worden beschreven.
// Option A: built-in (Node 20.6+), no import needed
// $ node --env-file=.env dist/index.js

// Option B: dotenv — must run first
import "dotenv/config";
import { EnvSchema } from "./env-schema";

export const env = EnvSchema.parse(process.env);

Het schema hergebruiken voor invoer van aanvragen

Exact dezelfde aanpak valideert onbetrouwbare runtime-invoer — aanvraagteksten, queryparameters en webhookgegevens. Netwerkgegevens zijn net zo ongetypeerd als process.env.

  • Definieer per eindpunt een schema en gebruik vervolgens parse voor de binnenkomende JSON.
  • Stuur bij een fout 400 terug in plaats van te crashen.
  • Het geparseerde resultaat is volledig getypeerd voor de rest van de afhandelaar.
import { z } from "zod";

const CreateUserBody = z.object({
  email: z.string().email(),
  age: z.coerce.number().int().min(0).optional(),
});

function handleCreateUser(rawBody, res) {
  const result = CreateUserBody.safeParse(rawBody);
  if (!result.success) {
    res.status(400).json({ errors: result.error.flatten() });
    return;
  }
  const body = result.data; // { email: string; age?: number }
  // ...persist body
}

Transformaties en afgeleide configuratie

Schemata kunnen waarden tijdens het parseren transformeren, zodat ze gebruiksklare configuratie opleveren. Zo blijft de conversielogica naast de validatieregel staan.

  • .transform() zet een gevalideerde waarde om naar een nieuwe vorm.
  • Omgevingsvariabelen met komma's worden arrays; vlaggen worden booleans.
  • Het afgeleide type weerspiegelt de getransformeerde uitvoer, niet de onbewerkte invoer.
import { z } from "zod";

const EnvSchema = z.object({
  // "a.com,b.com" -> ["a.com", "b.com"]
  CORS_ORIGINS: z.string()
    .transform((s) => s.split(",").map((o) => o.trim()))
    .pipe(z.array(z.string().url())),
  // "true"/"false" string -> boolean
  ENABLE_CACHE: z
    .enum(["true", "false"])
    .transform((v) => v === "true")
    .default("false"),
});

Regels tussen velden met refine

Soms hangt de geldigheid af van de relatie tussen velden — in productie moet bijvoorbeeld een geheim zijn ingesteld, terwijl lokaal een standaardwaarde volstaat. Gebruik .refine() of .superRefine() op het objectschema.

  • De controle wordt uitgevoerd nadat afzonderlijke velden zijn geparseerd.
  • Je koppelt een aangepast bericht en een path, zodat de fout naar het juiste veld verwijst.
  • Zo leg je bedrijfsregels vast die een vlak schema per veld niet kan uitdrukken.
import { z } from "zod";

const EnvSchema = z
  .object({
    NODE_ENV: z.enum(["development", "production"]),
    SENTRY_DSN: z.string().url().optional(),
  })
  .refine(
    (e) => e.NODE_ENV !== "production" || !!e.SENTRY_DSN,
    { message: "SENTRY_DSN is required in production", path: ["SENTRY_DSN"] }
  );

Een getypeerde configuratiesingleton exporteren

Het uiteindelijke doel: de rest van je codebase gebruikt process.env nooit rechtstreeks. In plaats daarvan importeert de code één gevalideerd, getypeerd env-object.

  • Door de toegang te centraliseren vult TypeScript elke sleutel automatisch aan en worden typefouten tijdens het compileren gemeld.
  • Een variabelenaam hernoemen vereist nog maar een wijziging in één bestand.
  • Tests kunnen een schema importeren en mockobjecten doorgeven in plaats van globale toestand te wijzigen.
// src/config.ts
import "dotenv/config";
import { z } from "zod";

const EnvSchema = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
  PORT: z.coerce.number().int().positive().default(3000),
  DATABASE_URL: z.string().url(),
});

const parsed = EnvSchema.safeParse(process.env);
if (!parsed.success) {
  console.error("❌ Invalid env:", parsed.error.flatten().fieldErrors);
  process.exit(1);
}

export const env = Object.freeze(parsed.data);
// elsewhere: import { env } from "./config";

Snelle controle

Test je begrip van het typeveilige configuratiepatroon.

Samenvatting

Je hebt geleerd hoe je configuratie en externe invoer typeveilig maakt in modern Node.js:

  • Waarom: process.env en netwerkgegevens zijn ongetypeerde tekenreeksen; valideer één keer en stop meteen bij fouten.
  • Schemata: een bibliotheek zoals Zod biedt runtimevalidatie en afgeleide statische typen vanuit één definitie (z.infer).
  • Omgevingsvariabelen laden: converteer getallen, beperk waarden met enums en standaardwaarden, en laad .env via --env-file of dotenv/config voordat je de waarden uitleest.
  • safeParse: maak error.flatten().fieldErrors op en gebruik bij fouten process.exit(1).
  • Verder dan env: hergebruik schemata voor aanvraagteksten, voeg .transform() toe voor afgeleide configuratie en .refine() voor regels tussen velden.
  • Resultaat: exporteer één bevroren, getypeerde env-singleton die de hele app importeert.
Gratis beginnen

Leer JavaScript met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
22
Lessen
92

Veelgestelde vragen

Is de les “Typeveilige omgevingsconfiguratie en runtimevalidatie” gratis?

Ja — de volledige tekst van “Typeveilige omgevingsconfiguratie en runtimevalidatie” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Bootcamp backendontwikkeling met Node.js wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Bootcamp backendontwikkeling met Node.js bevat in totaal 4 lessen.

Wat leer ik in “Typeveilige omgevingsconfiguratie en runtimevalidatie”?

Valideer omgevingsvariabelen en externe invoer tijdens runtime met schemabibliotheken die statische typen afleiden. Je oefent met Bootcamp backendontwikkeling met Node.js door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Bootcamp backendontwikkeling met Node.js te beginnen?

Ervaring vooraf is niet nodig. Bootcamp backendontwikkeling met Node.js op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.

Hoe lang duurt de les “Typeveilige omgevingsconfiguratie en runtimevalidatie”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Bootcamp backendontwikkeling met Node.js?

Ja. Elke les over Bootcamp backendontwikkeling met Node.js bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Migreren van CommonJS naar native ES-modules
  2. tsconfig configureren voor Node-backendprojecten
  3. Typeveilige omgevingsconfiguratie en runtimevalidatie
  4. Snel itereren met tsx, hot reload en sourcemaps
← Terug naar Bootcamp backendontwikkeling met Node.js