Arbeiten mit Umgebungsvariablen in Node.js

February 10, 2022
Autor:in:
Prüfer:in:

Arbeiten mit Umgebungsvariablen in Node.js


Hallo und Danke fürs Lesen! Dieser Blogpost ist eine Übersetzung von Working with Environment Variables in Node.js. Während wir unsere Übersetzungsprozesse verbessern, würden wir uns über Dein Feedback an help@twilio.com freuen, solltest Du etwas bemerken, was falsch übersetzt wurde. Wir bedanken uns für hilfreiche Beiträge mit Twilio Swag :)

Mit Umgebungsvariablen lassen sich verschiedene Aspekte der Node.js-Anwendung konfigurieren. Viele Cloud-Hosts (Heroku, Azure, AWS, now.sh usw.) und Node.js-Module verwenden Umgebungsvariablen. Hosts legen beispielsweise eine PORT-Variable fest, die angibt, auf welchem Port der Server lauschen soll, um richtig zu funktionieren. Module können je nach Wert der NODE_ENV-Variable ein unterschiedliches Verhalten aufweisen (z. B. Protokollierung).

Hier zeige ich einige meiner Tricks und Tools für Umgebungsvariablen in Node.js.

Die Grundlagen

Der Zugriff auf Umgebungsvariablen wird in Node.js bereits von Haus aus unterstützt. Wenn der Node.js-Prozess startet, erhältst du automatisch Zugriff auf alle vorhandenen Umgebungsvariablen, indem ein env-Objekt als Eigenschaft des globalen process-Objekts erstellt wird. Wenn du einen Blick auf das Objekt werfen willst, führe den Node.js REPL mit node in der Befehlszeile aus und gib Folgendes ein:

console.log(process.env);

Dieser Code sollte alle Umgebungsvariablen ausgeben, die diesem Node.js-Prozess bekannt sind. Du kannst auf eine bestimmte Variable genauso zugreifen wie auf jede andere Eigenschaft eines Objekts:

console.log('The value of PORT is:', process.env.PORT);

Wir sehen, dass der Wert von PORT auf dem Computer undefined ist. Cloud-Hosts wie Heroku oder Azure verwenden die PORT-Variable jedoch, um dir mitzuteilen, auf welchem Port der Server lauschen soll, damit das Routing richtig funktioniert. Wenn du also das nächste Mal einen Webserver einrichtest, solltest du den Port bestimmen, auf dem er lauschen soll. Überprüfe dazu zuerst PORT und gib ihm ansonsten einen Standardwert:

const app = require('http').createServer((req, res) => res.send('Ahoy!'));
const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

Die hervorgehobene Zeile nimmt den Wert PORT an, wenn er verfügbar ist, oder gibt den Standardwert 3000 als Fallback-Port zum Lauschen an. Versuche den Code auszuführen, indem du ihn in einer Datei wie server.js speicherst und Folgendes ausführst:

node server.js

Die Ausgabe sollte die Meldung Server is listening on port 3000 sein. Halte den Server mit Ctrl+C an und starte ihn mit dem folgenden Befehl neu:

PORT=9999 node server.js

Die Meldung sollte nun Server is listening on port 9999 sein, da die PORT-Variable für diese Ausführung durch PORT=9999 vor node temporär festgelegt wurde.

Da process.env einfach ein normales Objekt ist, können wir die Werte sehr einfach setzen bzw. überschreiben:

process.env.MY_VARIABLE = 'ahoy';

Der obige Code wird den Wert MY_VARIABLE setzen oder überschreiben. Beachte jedoch, dass dieser Wert nur während der Ausführung dieses Node.js-Prozesses festgelegt wird und nur in Child-Prozessen, die von diesem Prozess gespawnt werden, verfügbar ist. Vermeide nach Möglichkeit, Umgebungsvariablen zu überschreiben, und initialisiere lieber eine Konfigurationsvariable, wie im PORT-Beispiel gezeigt.

Explizites Laden von Variablen aus .env-Dateien

Wenn du an mehreren verschiedenen Node.js-Projekten auf einem Computer arbeitest, kann es sein, dass sich die Namen von Umgebungsvariablen überlappen. Zum Beispiel könnte es sein, dass verschiedene Messaging Apps verschiedene Twilio Messaging Service SIDs benötigen, beide jedoch TWILIO_MESSAGE_SERVICE_SID heißen. Um eine projektspezifische Konfiguration zu erreichen, empfiehlt sich die Verwendung von .env-Dateien. Diese Dateien erlauben es dir, eine Vielzahl von verschiedenen Umgebungsvariablen und deren Werte festzulegen.

Normalerweise sind diese Dateien in der Quellcodeverwaltung unerwünscht, füge also beim Erstellen .env zu deiner .gitignore-Datei hinzu. In vielen Twilio-Demo-Anwendungen sind .env.example-Dateien zu sehen, die du nach .env kopieren kannst, um die Werte dann selbst festzulegen. Eine .env.example -Datei oder eine ähnliche Datei zu haben, ist eine gängige Vorgehensweise, wenn du eine Vorlagendatei mit anderen Leuten im Projekt teilen willst.

Wie laden wir die Werte aus dieser Datei? Die einfachste Möglichkeit ist die Verwendung eines npm-Modul namens dotenv. Installiere einfach das Modul über npm:

npm install dotenv --save

Füge anschließend die folgende Zeile ganz oben in deiner Eingabedatei ein:

require('dotenv').config();

Dieser Code lädt automatisch die .env-Datei im Stammverzeichnis deines Projekts und initialisiert die Werte. Dabei werden alle Variablen übersprungen, die bereits festgelegt sind. Verwende keine .env-Dateien in deiner Produktionsumgebung. Lege Stattdessen die Werte lieber direkt auf dem jeweiligen Host fest. Umschließe daher deine Ladeanweisung mit einer if-Anweisung:

if (process.env.NODE_ENV !== 'production') {
  require('dotenv').config();
}

Mit diesem Code laden wir die .env-Datei nur, wenn der Server nicht im Produktionsmodus gestartet wird.

Das wollen wir uns jetzt in Aktion ansehen. Installiere dotenv in einem Verzeichnis wie oben gezeigt. Erstelle eine dotenv-example.js-Datei im gleichen Verzeichnis und füge die folgenden Zeilen darin ein:

console.log('No value for FOO yet:', process.env.FOO);

if (process.env.NODE_ENV !== 'production') {
  require('dotenv').config();
}

console.log('Now the value for FOO is:', process.env.FOO);

Erstelle anschließend im gleichen Verzeichnis eine Datei namens .env mit diesem Inhalt:

FOO=bar

Führe das Skript aus:

node dotenv-example.js

Die Ausgabe sollte wie folgt aussehen:

No value for FOO yet: undefined
Now the value for FOO is: bar

Wie du sehen kannst, wurde der Wert mittels dotenv geladen und definiert. Wenn du den gleichen Befehl erneut ausführst und dabei NODE_ENV auf production setzt, wirst du sehen, dass er undefined bleibt.

NODE_ENV=production node dotenv-example.js

Die Ausgabe sollte jetzt wie folgt aussehen:

No value for FOO yet: undefined
Now the value for FOO is: undefined

Wenn du deinen eigentlichen Code nicht ändern möchtest, kannst du auch das Argument -r von Node verwenden, um dotenv beim Ausführen des Skripts zu laden. Ändere deine dotenv-example.js-Datei:

console.log('The value for FOO is:', process.env.FOO);

Führe die Datei nun zunächst normal aus:

node dotenv-example.js

Das Skript muss ausgeben, dass der aktuelle Wert für FOO undefined ist. Führe es nun mit dem entsprechenden Flag aus, um dotenv anzufordern:

node -r dotenv/config dotenv-example.js

Infolgedessen ist FOO nun auf bar gesetzt, da die .env-Datei geladen wurde.

Wenn du mehr über dotenv erfahren möchtest, sieh in der entsprechenden Dokumentation nach.

Ein alternativer Weg zum Laden von .env-Dateien

Nun ist dotenv zwar großartig, doch eine bestimmte Sache hat mich persönlich während meines Entwicklungsprozesses gestört. Vorhandene env-Variablen werden nicht überschrieben und du kannst das auch nicht erzwingen.

Ich fand das enttäuschend und beschloss, mein eigenes Modul auf Basis von dotenv zu schreiben, um dieses Problem zu beheben und das Laden von Umgebungsvariablen zu vereinfachen. Dabei herausgekommen ist node-env-run oder nodenv. Es ist ein Befehlszeilen-Tool, das eine .env-Datei lädt, die Werte mit dotenv initialisiert und dann dein Skript ausführt.

Du kannst es global installieren, aber ich empfehle, es nur für Entwicklungszwecke und lokal für das Projekt zu verwenden. Führe für die Installation Folgendes aus:

npm install node-env-run --save-dev

Erstelle anschließend eine nodenv-example.js-Datei und platziere diesen Code darin:

console.log('The value for FOO is:', process.env.FOO);

Wie du sehen kannst, müssen wir hier noch nichts anfordern. Das ist nur die Anwendungslogik. Versuche zuerst die Ausführung mit node:

node nodenv-example.js

Dieser ausgeführte Code sollte The value for FOO is: undefined ausgeben. Versuche nun, node-env-run zu verwenden, indem du Folgendes ausführst:

node_modules/.bin/nodenv nodenv-example.js

Das Ergebnis sollte The value for FOO is: bar sein, da die .env-Datei geladen wurde.

node-env-run kann bestehende Werte überschreiben. Um dies in Aktion zu sehen, führe zuerst den folgenden Befehl aus:

FOO=foo node_modules/.bin/nodenv nodenv-example.js

Die Befehlszeilenausgabe sollte Folgendes enthalten: The value for FOO is: foo. Wenn wir nun den Force-Modus aktivieren, können wir bestehende Werte überschreiben:

FOO=foo node_modules/.bin/nodenv --force nodenv.js

Folglich sollten wir wieder bei dem ursprünglichen The value for FOO is: bar sein.

Wenn du dieses Tool regelmäßig nutzen willst, empfehle ich dir, es in ein npm-Skript zu packen. Füge es dazu auf folgende Weise im package.json hinzu:

{
  "name": "twilio-blog",
  "version": "1.0.0",
  "description": "",
  "main": "nodenv-example.js",
  "scripts": {
    "start": "node .",
    "start:dev": "nodenv -f ."
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "node-env-run": "^2.0.1"
  }
}

Du kannst dann einfach Folgendes ausführen:

npm run start:dev

Wenn du mehr über node-env-run erfahren möchtest, solltest du dir die Dokumentation ansehen.

Umgebungsvariablen && npm-Skripte

In manchen Szenarien ist es sinnvoll, den Wert einer Umgebungsvariable zu überprüfen, bevor wir die Node.js-Anwendung in npm-Skripten öffnen. Das gilt zum Beispiel, wenn du node-env-run in einer Entwicklungsumgebung verwenden möchtest, node sich jedoch im production-Modus befindet. Ein Tool, das dies alles vereinfacht, ist if-env. Führe für die Installation Folgendes aus:

npm install if-env --save

Achte darauf, dass du es nicht als „dev dependency“ installierst, da wir es auch in der Produktion benötigen werden.

Ändere nun einfach deine npm-Skripte in deiner package.json-Datei:

  "scripts": {
    "start": "if-env NODE_ENV=production ?? npm run start:prod || npm run start:dev",
    "start:dev": "nodenv -f .",
    "start:prod": "node ."
  }

Dieses Skript wird nun npm run start:prod und anschließend node . ausführen, wenn NODE_ENV den Wert production hat, andernfalls wird es npm run start:dev und anschließend nodenv -f . ausführen. Du kannst diese Technik auch mit jeder anderen Umgebungsvariablen anwenden.

Probiere es aus, indem du Folgendes ausführst:

# should output "The value of FOO is: bar"
npm start

# should output "The value of FOO is: undefined"
NODE_ENV=production npm start

Wenn du mehr über if-env erfahren möchtest, dann schau dir die Dokumentation an.

Fehlerbehebung

Wir alle kennen den Moment, in dem es nicht so läuft wie gewünscht und ein Modul nicht das tut, was es tun soll. In so einem Moment ist es an der Zeit für eine Fehlerbehebung. Eine Strategie, die mir sehr geholfen hat, ist die Verwendung der DEBUG-Umgebungsvariable, um ausführliche Protokolle für eine Reihe von Modulen zu erhalten. Nimm zum Beispiel einen einfachen express-Server wie diesen:

const app = require('express')();
const bodyParser = require('body-parser');
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

app.post('/', (req, res, next) => {
console.log(req);
});

app.listen(3000);

Starte ihn mit der DEBUG-Variable auf *:

DEBUG=* node server.js

Dann erhältst du ein umfangreiches Protokoll, das ungefähr so aussieht:

Screen Shot

Der Clou dabei ist ein leichtgängiges Modul namens debug und seine Verwendung ist super einfach. Initialisiere es ganz einfach mit einem „namespace“, wenn du es nutzen möchtest. Danach kannst du in diesem Namensraum protokollieren. Wenn jemand die Ausgabe anzeigen möchte, muss er nur den Namensraum in der DEBUG -Variable aktivieren. express verwendet in diesem Fall zahlreiche untergeordnete Namensräume. Wenn du alles vom express-Router haben möchtest, musst du nur DEBUG mit dem entsprechenden Platzhalter setzen:

DEBUG=express:router* node server.js

Wenn du debug in deinem eigenen Modul verwenden möchtest, musst du es nur zuerst installieren:

npm install debug --save

Und es anschließend auf folgende Weise erfassen:

const debug = require('debug')('myApp:someComponent');

debug('Here is a pretty object %o', { someObject: true });

Wenn du mehr über debug erfahren möchtest, schau dir die entsprechende Dokumentation an.

Verwende jetzt alle Umgebungsvariablen!

Das ist bei weitem nicht alles, was du mit Umgebungsvariablen und allen vorhandenen Tools machen kannst, sondern nur die Möglichkeiten, die ich am häufigsten nutze. Wenn du noch andere tolle Tools hast, die du regelmäßig nutzt, würde ich mich freuen, davon zu hören!

Nachdem du nun ein wenig mehr über Umgebungsvariablen in Node.js erfahren hast, probiere die Node.js-Schnellstarts von Twilio aus!