mitmario.dev

Umgebungsvariablen

Node.js Sandbox 4 Min Lesezeit 3 BeispieleLektion 2 von 6

Argumente sind für das, was sich bei jedem Aufruf ändert. Für das, was sich pro Ort ändert, gibt es einen zweiten Weg.

Dein Programm soll auf drei Rechnern laufen: auf deinem, auf einem Testsystem und in der Produktion. Überall gilt eine andere Datenbankadresse, ein anderer Port, ein anderer Schlüssel für einen fremden Dienst. Der Code soll trotzdem überall derselbe sein, Zeichen für Zeichen. Sonst hast du drei Fassungen, und eine davon ist immer die falsche.

Das Fach, in das die Umgebung ihre Werte legt

process.env ist ein Objekt mit allem, was die Umgebung deinem Prozess mitgegeben hat. Auf der Kommandozeile schreibst du die Werte vor den Befehl:

PORT=8080 node start.mjs

Ein Wert mit Rückfall auf den Standard
const port = Number(process.env.PORT) || 3000;
const umgebung = process.env.NODE_ENV ?? "development";

console.log("Port:", port);
console.log("Typ des Rohwerts:", typeof process.env.PORT);
console.log("Umgebung:", umgebung);

Genau so läuft dieses Beispiel auch, mit PORT=8080 davor; im Terminal steht die Befehlszeile über der Ausgabe. Zwei Dinge stehen darin, und beide sind wichtig.

Erstens: typeof process.env.PORT ist string, nicht number. Dazu gleich mehr.

Zweitens: || 3000 fängt den Fall ab, dass niemand einen Port gesetzt hat. Das ist kein Notbehelf, sondern gehört dazu. Ein Programm, das ohne gesetzte Umgebung gar nicht erst startet, ist auf dem eigenen Rechner mühsam.

Derselbe Code ohne gesetzten Port
const port = Number(process.env.PORT) || 3000;
const umgebung = process.env.NODE_ENV ?? "development";

console.log("Port:", port);
console.log("Typ des Rohwerts:", typeof process.env.PORT);
console.log("Umgebung:", umgebung);

Es ist dieselbe Datei, unverändert, nur ohne PORT=8080 davor. Eine nicht gesetzte Variable ist undefined, und dafür springt der Standardwert ein. Beim NODE_ENV darüber siehst du dasselbe schon im ersten Beispiel: Gesetzt ist dort nur der Port, also steht bei der Umgebung development.

Im Terminal kannst du beide Fassungen selbst nebeneinanderstellen. Starte die Sandbox und tipp erst node start.mjs, dann PORT=8080 NODE_ENV=production node start.mjs. Die Werte stehen vor dem Befehl und gelten nur für ihn, der nächste Aufruf weiß nichts mehr davon.

Warum jeder Wert Text ist

Die Umgebung kennt keine Datentypen. Sie reicht Zeichenketten durch, und was du damit machst, ist deine Sache. PORT=8080 kommt als "8080" an, nicht als 8080.

Bei Zahlen fällt das schnell auf, weil Number(...) sich anbietet. Bei Wahrheitswerten fällt es gar nicht auf, und das ist die Falle.

Die Falle mit dem Wahrheitswert
const rohwert = process.env.DEBUG;

console.log("Rohwert:", rohwert);
console.log("Typ:", typeof rohwert);
console.log("Falsch geprueft:", rohwert ? "an" : "aus");
console.log("Richtig geprueft:", rohwert === "true" ? "an" : "aus");

Lies den dritten Fall noch einmal. DEBUG steht auf false, und die Prüfung sagt trotzdem an.

Der Grund ist keine Merkwürdigkeit von Node, sondern eine Regel von JavaScript: In einem if ist jede Zeichenkette wahr, außer der leeren. "false" ist eine Zeichenkette mit fünf Zeichen, also wahr.

Die Regel dazu lautet: vergleiche Umgebungswerte immer ausdrücklich. process.env.DEBUG === "true" sagt genau, was du meinst. if (process.env.DEBUG) sagt nur, ob überhaupt etwas gesetzt ist, und das ist so gut wie nie die Frage.

Die .env-Datei

Sechs Variablen vor jedem Befehl zu tippen hält niemand durch. Deshalb legt man sie in eine Datei, eine Zeile je Wert, und Node liest sie beim Start ein:

node --env-file=.env start.mjs

Das ist eingebaut, du brauchst kein Paket dafür. Früher war dotenv dafür Standard, und in bestehenden Projekten wirst du es weiter finden.

Jetzt der Satz, auf den es in diesem Abschnitt am meisten ankommt:

Die .env gehört in die .gitignore. Immer. Ohne Ausnahme.

In dieser Datei stehen Zugangsdaten, und ein Repository ist dafür der falsche Ort. Es ist auch nicht damit getan, sie später wieder herauszunehmen: Was einmal committet war, steht in der Historie, und wer den Schlüssel abgreifen will, sucht genau dort. Abschnitt 15 geht darauf ein, wie man damit umgeht, wenn es doch passiert ist. Lektion 5.4 zeigt, wie so eine .gitignore aussieht.

Was stattdessen ins Repository gehört, ist eine .env.example mit denselben Schlüsseln und leeren oder erfundenen Werten. Sie ist die Liste dessen, was gesetzt sein muss, und die kann jeder sehen.

Wo der Standardwert hingehört

Ein Standardwert steht an der Stelle, an der die Variable gelesen wird, und zwar genau einmal. Wenn process.env.PORT an vier Stellen in deinem Code auftaucht, hast du vier Gelegenheiten, den Standard unterschiedlich zu schreiben.

Der übliche Weg ist eine kleine Datei, die alle Werte einmal einliest, prüft und als fertiges Objekt weitergibt. Der restliche Code fragt dann dieses Objekt und nie mehr process.env. Lektion 18.4 baut so eine Datei, und sie hat einen angenehmen Nebeneffekt: Fehlt eine Pflichtvariable, merkt das dein Programm beim Start und nicht mitten in der Nacht bei der ersten Anfrage.

Zum Mitnehmen

Jeder Wert in process.env ist Text. Auch „8080“, auch „false“. Und der Text „false“ ist in einem if wahr.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 3 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, dein Code läuft auf einem Server

    Öffnet sich mit dem Basis Konto.