Bootcamp i backendudvikling med Node.js · Lektion

Typesikker miljøkonfiguration og runtime-validering

Validér miljøvariabler og eksternt input ved runtime med skemabiblioteker, der udleder statiske typer.

Lektion 3 af 413 trin

Typesikker miljøkonfiguration og runtime-validering er en gratis Bootcamp i backendudvikling med Node.js-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Bootcamp i backendudvikling med Node.js, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Bootcamp i backendudvikling med Node.js-kurset indeholder 4 lektioner i alt.

Hvorfor validere process.env?

I Node.js er alle værdier i process.env en streng eller undefined — køretiden giver dig ingen garantier.

  • process.env.PORT kan være "3000", "" eller mangle helt.
  • En slåfejl som DATABSE_URL giver ubemærket værdien undefined.
  • TypeScript typer process.env som Record<string, string | undefined>, så den kan ikke finde manglende nøgler.

Hvis du læser konfigurationen tilfældigt rundt omkring i din app, opstår nedbrud dybt inde i forespørgselshåndteringer — længe efter opstart. Løsningen er at validere én gang ved opstart og stoppe hurtigt med en tydelig meddelelse.

// 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

Skemabiblioteker og typeudledning

Et skemabibliotek giver dig mulighed for at beskrive datas form og begrænsninger én gang, derefter validere ved kørsel OG udlede en statisk TypeScript-type fra den samme definition.

  • Populære valg: Zod, Valibot, ArkType, TypeBox.
  • Én sandhedskilde — skemaet — leverer både kontrollen ved kørsel og typen ved kompilering.
  • Ingen duplikeret interface, der kan komme ud af trit.

Vi bruger Zod, som er det mest almindelige valg i Node.js-økosystemet. z.infer<typeof schema> udleder den type, som skemaet validerer.

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 }

Et første miljøskema

Lad os beskrive det miljø, som vores tjeneste har brug for. Da værdierne i process.env altid er strenge, skal skemaet konvertere numeriske felter og begrænse strengfelter.

  • z.coerce.number() omdanner "3000" til 3000.
  • z.enum([...]) begrænser en værdi til et fast sæt.
  • Kædning som .min() / .url() tilføjer begrænsninger ved kørsel.

Definér skemaet i sit eget modul (for eksempel src/env.ts), så det importeres én gang ved opstart.

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),
});

Parsing ved opstart

Kald schema.parse(process.env) én gang øverst i din startfil. Hvis valideringen mislykkes, kaster Zod en ZodError, og processen afsluttes, før den modtager trafik.

  • parse() returnerer et fuldt typet og valideret objekt.
  • Konverterede og standardiserede værdier er allerede anvendt.
  • Eksportér resultatet, så resten af appen importerer et typet env-objekt i stedet for at tilgå process.env direkte.
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 til venlige fejlmeddelelser

En rå staksporingsrapport fra ZodError er støjende. Brug safeParse til at få et resultatobjekt, som du kan formatere til en letlæselig opstartsmeddelelse, og afslut derefter bevidst.

  • safeParse returnerer { success: true, data } eller { success: false, error } — den kaster aldrig en fejl.
  • error.flatten().fieldErrors grupperer meddelelser pr. felt.
  • Afslut med process.exit(1), så orkestratorer (Docker, PM2, k8s) registrerer en mislykket opstart.
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;

Et selvstændigt, kørbart eksempel

Her er en selvstændig demonstration af mønsteret med validering efterfulgt af typeudledning. Den bruger et almindeligt objekt i stedet for process.env, så en online-dommer kan køre den uden opsætning.

Den viser konvertering, en standardværdi, en enum og en venlig fejlsti — de samme teknikker, som du ville bruge til indlæsning af et rigtigt miljø.

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

Indlæsning af .env-filer med ESM

Lokalt gemmer du konfiguration i en .env-fil. Biblioteket dotenv læser den ind i process.env. Med ESM og TypeScript skal du indlæse den før ethvert modul, der læser konfiguration.

  • Moderne Node (v20.6+) har et indbygget flag, --env-file=.env — ingen afhængighed er nødvendig.
  • Hvis du bruger dotenv, skal du kalde dotenv/config helt øverst, fordi ESM-importer løftes og evalueres først.
  • Commit aldrig rigtige hemmeligheder; commit en .env.example, der dokumenterer de nødvendige nøgler.
// 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);

Genbrug af skemaet til input fra forespørgsler

Præcis den samme tilgang validerer usikkert input fra kørselstiden — forespørgselsbrødtekster, forespørgselsparametre og webhook-nyttedata. Netværksdata er lige så utypede som process.env.

  • Definér et skema pr. slutpunkt, og kør derefter parse på den indkommende JSON.
  • Ved fejl skal du svare med 400 i stedet for at få processen til at gå ned.
  • Det parsede resultat er fuldt typet for resten af håndteringen.
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
}

Transformationer og afledt konfiguration

Skemaer kan transformere værdier under parsing og dermed producere konfiguration, der er klar til brug. Det holder konverteringslogikken tæt på valideringsreglen.

  • .transform() omdanner en valideret værdi til en ny form.
  • Miljøstrenge med kommaseparerede værdier bliver til arrays; flag bliver til boolske værdier.
  • Den udledte type afspejler det transformerede output, ikke det rå input.
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"),
});

Regler på tværs af felter med refine

Nogle gange afhænger gyldigheden af forholdet mellem felter — for eksempel skal en hemmelighed være angivet i produktion, mens en standardværdi er fin lokalt. Brug .refine() eller .superRefine() på objektskemaet.

  • Kontrollen køres, efter at de enkelte felter er parset.
  • Du knytter en brugerdefineret meddelelse og en path til, så fejlen peger på det rigtige felt.
  • På den måde udtrykker du forretningsregler, som et fladt skema med regler pr. felt ikke kan udtrykke.
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"] }
  );

Eksport af en typet konfigurationssingleton

Målet er, at resten af din kodebase aldrig tilgår process.env. Den importerer ét valideret og typet env-objekt.

  • Centraliseret adgang betyder, at TypeScript kan foreslå alle nøgler og markere slåfejl ved kompilering.
  • Hvis du omdøber en variabel, kræver det kun en ændring i én fil.
  • Test kan importere et skema og give det mockobjekter i stedet for at ændre global tilstand.
// 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";

Hurtigt tjek

Test din forståelse af mønsteret for typesikker konfiguration.

Opsummering

Du har lært at gøre konfiguration og eksternt input typesikkert i moderne Node.js:

  • Hvorfor: process.env og netværksdata er utypede strenge; validér én gang og stop hurtigt ved fejl.
  • Skemaer: et bibliotek som Zod giver validering ved kørsel samt udledte statiske typer fra én definition (z.infer).
  • Indlæsning af miljø: konvertér tal, begræns med enum-værdier og standardværdier, og indlæs .env via --env-file eller dotenv/config før læsning.
  • safeParse: formatér error.flatten().fieldErrors, og kald process.exit(1) ved fejl.
  • Ud over miljøet: genbrug skemaer til forespørgselsbrødtekster, tilføj .transform() til afledt konfiguration og .refine() til regler på tværs af felter.
  • Resultat: eksportér én frosset, typet env-singleton, som hele appen importerer.
Gratis at komme i gang

Lær JavaScript med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
22
Lektioner
92

Ofte stillede spørgsmål

Er lektionen “Typesikker miljøkonfiguration og runtime-validering” gratis?

Ja — hele teksten til “Typesikker miljøkonfiguration og runtime-validering” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Bootcamp i backendudvikling med Node.js-kurset, skal du opgradere til CoddyKit PRO. Bootcamp i backendudvikling med Node.js-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Typesikker miljøkonfiguration og runtime-validering”?

Validér miljøvariabler og eksternt input ved runtime med skemabiblioteker, der udleder statiske typer. Du øver dig i Bootcamp i backendudvikling med Node.js med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Bootcamp i backendudvikling med Node.js?

Der kræves ingen tidligere erfaring. Bootcamp i backendudvikling med Node.js på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Typesikker miljøkonfiguration og runtime-validering”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Bootcamp i backendudvikling med Node.js-lektion?

Ja. Alle Bootcamp i backendudvikling med Node.js-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Migrering fra CommonJS til native ES-moduler
  2. Konfiguration af tsconfig til Node-backendprojekter
  3. Typesikker miljøkonfiguration og runtime-validering
  4. Hurtig iteration med tsx, hot reload og source maps
← Tilbage til Bootcamp i backendudvikling med Node.js