Node.js's integrierte Fetch-Funktion und ihre Undici-Grundlage entschlüsselt
Ethan Miller
Product Engineer · Leapcell

Einleitung
Jahrelang beinhaltete das Senden von HTTP-Anfragen in Node.js oft externe Bibliotheken wie axios oder node-fetch. Während diese Bibliotheken der Community gute Dienste geleistet haben, führten sie eine zusätzliche Abhängigkeit und manchmal eine leichte Lernkurve für Entwickler ein, die an die native fetch-API des Browsers gewöhnt waren. Mit der Veröffentlichung von Node.js 18 wurde ein bedeutender Meilenstein erreicht: Eine globale, integrierte fetch-API, die ihrem Gegenstück im Browser nachempfunden ist, wurde ohne externe Installationen verfügbar. Diese Integration optimiert die Webentwicklung in Node.js drastisch, bietet eine vertraute Schnittstelle für Datenabrufe und verspricht verbesserte Leistung und Stabilität. Dieser Artikel wird das native fetch von Node.js gründlich untersuchen, seine zugrunde liegende Architektur aufdecken und insbesondere seine tiefgreifende Beziehung zu undici, dem hochmodernen HTTP/1.1-Client, der es antreibt, untersuchen.
Kernkonzepte erklärt
Bevor wir uns mit den Besonderheiten von fetch und undici befassen, lassen Sie uns einige grundlegende Begriffe klären, die für unsere Diskussion zentral sein werden:
fetchAPI: Eine moderne, Promise-basierte API für Netzwerkanfragen, die häufig zum Abrufen von Ressourcen über das Netzwerk verwendet wird. Sie ist flexibler und leistungsfähiger alsXMLHttpRequest.undici: Ein Hochleistungs-HTTP/1.1-Client für Node.js, der mit der WHATWGfetch-API kompatibel ist und von Grund auf auf Geschwindigkeit und Zuverlässigkeit ausgelegt ist. Er hat zum Ziel, der Standard-HTTP-Client für Node.js zu sein.- WHATWG 
fetchStandard: Eine Spezifikation der Web Hypertext Application Technology Working Group (WHATWG), die diefetch-API definiert. Das integriertefetchvon Node.js strebt eine enge Ausrichtung an diesem Standard an. - Streams: Ein grundlegendes Konzept in Node.js zur Verarbeitung von Daten in Chunks. 
fetchnutzt Streams sowohl für Request-Bodies als auch für Response-Bodies, was eine effiziente Verarbeitung großer Datenmengen ermöglicht. - Request/Response-Objekte: Die Kernobjekte, die von der 
fetch-API verwendet werden. EinRequest-Objekt repräsentiert eine ausgehende Netzwerkanfrage und einResponse-Objekt eine eingehende Netzwerkantwort. 
Die Funktionsweise von Node.js Fetch
Die integrierte fetch-API von Node.js ist keine vollständig unabhängige Implementierung. Vielmehr ist sie ein direkter Re-Export und eine leichte Modifikation der undici-Bibliothek. Diese strategische Entscheidung des Node.js-Core-Teams bietet mehrere Vorteile:
- Standardkonformität: 
undiciist sorgfältig darauf ausgelegt, dem WHATWGfetch-Standard zu entsprechen, wodurch sichergestellt wird, dass Node.js-Entwickler eine API erhalten, die konsistent mit Browser-Umgebungen funktioniert. Dies reduziert den kognitiven Aufwand und erleichtert isomorphe Codebasen. - Leistung und Effizienz: 
undiciist bekannt für seine außergewöhnliche Leistung. Es verfügt über einen benutzerdefinierten Request/Response-Parser, Connection Pooling, Pipelining und andere Optimierungen, die den Overhead erheblich reduzieren und den Durchsatz im Vergleich zu älteren HTTP-Clients verbessern. Durch die Nutzung vonundicierbt Node.jsfetchdiese Leistungsvorteile. - Aktive Entwicklung: 
undiciist ein aktiv entwickeltes Projekt, das kontinuierliche Verbesserungen, Fehlerbehebungen und Funktionserweiterungen gewährleistet. Die Integration ermöglicht es Node.js, diese Fortschritte schnell zu übernehmen. 
Beispiel für die Grundverwendung
Die Verwendung von fetch in Node.js 18+ ist genauso einfach wie im Browser:
// my-data-fetcher.js async function fetchData(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); // oder .text(), .blob(), .arrayBuffer(), .formData() console.log('Fetched data:', data); return data; } catch (error) { console.error('Error fetching data:', error); throw error; } } // Beispielverwendung: fetchData('https://jsonplaceholder.typicode.com/todos/1') .then(data => console.log('Successfully retrieved:', data)) .catch(error => console.error('Failed to retrieve:', error.message)); // Beispiel mit POST-Anfrage und benutzerdefinierten Headern async function postData(url, data) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); console.log('POST successful:', result); return result; } catch (error) { console.error('Error posting data:', error); throw error; } } postData('https://jsonplaceholder.typicode.com/posts', { title: 'foo', body: 'bar', userId: 1, }) .then(data => console.log('New post created:', data)) .catch(error => console.error('Failed to create post:', error.message));
Um diesen Code auszuführen, speichern Sie ihn einfach als my-data-fetcher.js und führen Sie node my-data-fetcher.js in Ihrem Terminal aus. Sie werden sehen, wie die Daten abgerufen und in der Konsole protokolliert werden.
Wie undici fetch unterstützt
Wenn Sie in Node.js 18+ global.fetch() aufrufen, rufen Sie im Wesentlichen undici.fetch() auf. Die Integration ist nahtlos. undici stellt die Kernfunktionalität des HTTP-Clients bereit und kümmert sich um Connection Management, Request-Serialisierung, Response-Parsing und Fehlerbehandlung.
Betrachten wir einen vereinfachten konzeptionellen Ablauf:
fetch-Aufruf: Wennfetch(url, options)aufgerufen wird, empfängt die globalefetch-Funktion (dieundici.fetchist) diese Argumente.- Erstellung des 
Request-Objekts:undicierstellt intern einRequest-Objekt basierend auf der bereitgestellten URL und den Optionen und hält sich dabei an die WHATWGfetch-Spezifikation. - Connection Management: 
undicinutzt einen ausgeklügelten Mechanismus für Connection Pooling. Es gibt bestehende HTTP/1.1-Verbindungen wieder oder baut neue effizient auf. - Senden der Anfrage: Das 
Request-Objekt wird in eine HTTP-Nachricht serialisiert und über die aufgebaute Verbindung gesendet.undicikümmert sich um Aspekte wie Header, Body-Encoding und Weiterleitungen. - Empfangen und Parsen der Antwort: Nach Erhalt der Serverantwort analysiert 
undicidie eingehende HTTP-Nachricht schnell, erstellt einResponse-Objekt und stellt den Antwort-Body alsReadableStreamzur Verfügung. - Rückgabe des 
Response-Objekts: Derfetch-Aufruf wird mit demResponse-Objekt aufgelöst, was Ihnen ermöglicht, auf Eigenschaften wiestatus,headersund Methoden wiejson()odertext()zuzugreifen, um den Body zu konsumieren. Diese Body-Methoden beinhalten oft das Verbrauchen des zugrunde liegendenundici-Streams. 
Erweiterte fetch-Funktionen und die Rolle von undici
undici stellt über fetch hinaus eine Low-Level-API bereit, auf die zugegriffen werden kann, wenn eine direkte Kontrolle über das Verhalten des HTTP-Clients benötigt wird. Zum Beispiel bietet undici.Agent eine feingranulare Kontrolle über Connection Pooling, Timeouts und Weiterleitungen.
Obwohl global.fetch oft ausreicht, kann für Szenarien, die benutzerdefinierte Connection Agents oder fortschrittliche Pooling-Strategien erfordern, die direkte Verwendung von undici in Betracht gezogen werden. Die fetch-API selbst bietet jedoch eine robuste Reihe von Optionen innerhalb ihres init-Objekts, die die meisten gängigen Anwendungsfälle abdecken, wie zum Beispiel:
signal: Abbruch von Anfragen mitAbortController.const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 5000); // Nach 5 Sekunden abbrechen fetch('https://slow-api.example.com/data', { signal: controller.signal }) .then(response => { clearTimeout(timeoutId); return response.json(); }) .then(data => console.log(data)) .catch(err => { if (err.name === 'AbortError') { console.error('Fetch aborted by user or timeout'); } else { console.error('Fetch error:', err); } });redirect: Steuerung des Weiterleitungsverhaltens (follow,error,manual).bodymit Streams: Effizientes Senden großer Datenmengen, ohne den gesamten Payload im Speicher zu puffern.const { Readable } = require('stream'); async function uploadStreamData() { const readableStream = new Readable({ read() { this.push('Hello '); this.push('World!'); this.push(null); // Keine weiteren Daten } }); try { const response = await fetch('https://httpbin.org/post', { method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: readableStream // Node.js fetch kann Readable Streams direkt akzeptieren }); const data = await response.json(); console.log('Stream upload response:', data); } catch (error) { console.error('Stream upload error:', error); } } uploadStreamData();
Die fetch-API von Node.js unterstützt dank undici diese erweiterten Mechanismen und ist somit ein leistungsfähiges und vielseitiges Werkzeug für die Netzwerkkommunikation in serverseitigen Anwendungen.
Fazit
Die Integration der fetch-API in Node.js 18+ stellt einen bedeutenden Fortschritt für die Plattform dar und bietet Entwicklern eine vertraute, leistungsfähige und performante Möglichkeit, HTTP-Anfragen zu stellen. Dieses nahtlose Erlebnis ist größtenteils auf undici, den hochmodernen HTTP/1.1-Client, zurückzuführen, der als Rückgrat des nativen fetch von Node.js fungiert. Durch die Nutzung von undici erzielt Node.js fetch nicht nur hohe Leistung und strenge Einhaltung des WHATWG fetch-Standards, sondern bietet auch eine robuste Grundlage für die moderne Webentwicklung. Node.js fetch vereinfacht HTTP-Interaktionen und ermöglicht es Entwicklern, sich stärker auf die Anwendungslogik und weniger auf die Komplexität von HTTP-Clients zu konzentrieren.