ProgrammierungWeiterführendesFetch für Web-Daten

fetch: Daten aus dem Web

💡

Dauer: ca. 45 Minuten

  • JavaScript im Browser hat eine fetch-Funktion für Web-Daten
  • node-fetch stellt diese in NodeJS bereit (ab NodeJS 18 nativ!)
  • fetch ist asynchron und liefert Promises
  • async/await vereinfacht den Umgang mit Promises

Ziel: Daten von Webservice-APIs einbinden

Am Beispiel des Wetterdienstes Open-Meteo wollen wir die aktuelle Temperatur für Berlin abfragen.

Die kostenlose und frei zugängliche API (application programming interface) von Open-Meteo kann dazu mit folgender URL (Web-Link) angefragt werden:

https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=true

Die Koordinaten für Berlin (Breitengrad 52.51, Längengrad 13.41) werden als Parameter in der URL übermittelt, sodass der Server die gewünschte Anfrage ausführen und das Ergebnis als JSON-Daten liefern kann.

Im Browser steht JavaScript eine Funktion fetch zur Verfügung, mit der Daten aus dem Web via URL z.B. als JSON angefordert werden können.

⟶ URL im Browser laden und fetch mit open-meteo.com in der Browser-Konsole vorführen.

node-fetch

Ab Version 18 ist fetch in NodeJS enthalten (vorerst als experimentelles Feature).

In NodeJS bzw. replit.com kann die Funktionalität von fetch durch die Installation der Bibliothek node-fetch dem Projekt hinzugefügt werden.

Danach kann fetch wie folgt zur Verwendung im Code importiert werden:

import fetch from 'node-fetch';

fetch verwenden

Nun kann fetch in replit.com bzw. NodeJS wie im Browser verwendet werden.

Da ein Netzwerkzugriff mit fetch unter Umständen etwas länger dauern kann (z.B. bei langsamer Internetverbindung), läuft fetch asynchron bzw. „im Hintergrund“ ab.

Das Ergebnis von fetch ist daher ein Promise-Objekt, das mit Callback-Funktionen asynchron verarbeitet werden muss.

Promises stellen ein etwas schwer zugängliches Thema in JavaScript dar. Für die Verwendung von fetch besprechen wir hier nur das Nötigste.

Die Verwendung von fetch und Promises läuft folgendermaßen ab:

  • fetch mit URL aufrufen, dies liefert ein Promise-Objekt
  • das Promise-Objekt enthält eine response, die mit der Methode json() die JSON-Daten liefert
  • response im Promise-Objekt kann mit then ausgelesen werden — hierbei wird ein Callback in then benötigt
  • then liefert wieder ein Promise-Objekt, das das JavaScript-Objekt basierend auf den JSON-Daten enthält
  • dieses kann wiederum mit then und Callback-Funktion verarbeitet werden

Der Code in der Funktion getBerlinWeatherPromiseSteps() zeigt, wie dies umgesetzt wird:

import fetch from 'node-fetch';
 
function getBerlinWeatherPromiseSteps() {
  const url = 
    `https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=true`;
 
  // 1. Schritt: fetch ausführen
  // --> Das Ergebnis ist ein Promise-Objekt
  const fetchPromise = fetch(url);
 
  // 2. Schritt: aus dem fetchPromise die 
  // JSON-Daten durch response.json() mit Callback in then() auslesen
  // --> then liefert wieder ein Promise-Objekt
  const dataPromise = 
    fetchPromise.then(response => response.json());
 
  // 3. Schritt: aus dem dataPromise die relevanten Daten mit then() ausgeben
  dataPromise.then(data => console.log(data.current_weather.temperature));
}

Da die einzelnen Aufrufe von then wiederum Promises liefern, können diese direkt miteinander kombiniert werden (dies wird „chaining“ genannt):

function getBerlinWeatherPromiseChained() {
  const url = 
    `https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=true`;
  fetch(url)
    .then(response => response.json())
    .then(data => console.log(data.current_weather.temperature));
}

Somit ist die Verwendung von fetch bereits einigermaßen kompakt und übersichtlich möglich.

Promises mit then kann schnell zu unübersichtlichem Code führen. Alternativ kann async/await zum Einsatz kommen.

async/await für Promises

Mit async/awaitwird die Verarbeitung von Promises leichter nachvollziehbarer.

Der Code der obigen Funktionen kann ebenso mit async/await ausgedrückt werden. Dazu ist eine Funktion mit dem Schlüsselwort async vor function zu deklarieren:

// Die Funktion ist nun asynchron!
async function getBerlinWeatherAwait() {
  const url = 
    `https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&current_weather=true`;
  const response = await fetch(url);
  const data = await response.json();
  console.log(data.current_weather.temperature);
  return data.current_weather.temperature;
}

await löst einen Promise auf und liefert das Ergebnis zurück. Gleichzeitig „wartet“ (await) der Code auf die Verarbeitung, sodass der Code einem „normalem“ synchronen Ablauf syntaktisch ähnelt.

🚫

await ist nur dann in einer Funktion erlaubt, wenn diese mit async deklariert wurde!

Der Rückgabewert einer async-Funktion ist wiederum ein Promise, der automatisch von JavaScript erzeugt wird. Um den „eigentlichen“ Rückgabewert im Promise einer async-Funktion zu verwenden, muss dieser mit await in einer weiteren async-Funktion angefragt werden:

async function berlinTemperature() {
  const temp = await getBerlinWeatherAwait();
  console.log("Temperatur in Berlin: " + temp);
}
berlinTemperature();
⚠️

Beim Zugriff auf APIs bzw. Webservices mit fetch können verschiedene Fehlersituationen eintreten:

  • keine Internetverbindung,
  • Server ist nicht erreichbar,
  • Antwort des Servers passt nicht zum erwarteten Ergebnis,
  • u.a.

Daher sollte ein Webzugriff mit fetch z.B. mit try…catch-Blöcken umgeben werden, um Fehlersituationen im Code zu behandeln.

Dieser Artikel beschreibt ausführlich, was bei der Verwendung von fetch zu beachten ist, und zeigt praktische Code-Beispiele: www.builder.io/blog/safe-data-fetching