Bun implementiert den WHATWG fetch-Standard mit einigen Erweiterungen, um den Anforderungen von serverseitigem JavaScript gerecht zu werden.
Bun implementiert auch node:http, aber fetch wird generell empfohlen.
Senden einer HTTP-Anfrage
Um eine HTTP-Anfrage zu senden, verwenden Sie fetch:
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // oder response.json(), response.formData(), etc.fetch funktioniert auch mit HTTPS-URLs.
const response = await fetch("https://example.com");Sie können fetch auch ein Request-Objekt übergeben.
const request = new Request("http://example.com", {
method: "POST",
body: "Hallo, Welt!",
});
const response = await fetch(request);Senden einer POST-Anfrage
Um eine POST-Anfrage zu senden, übergeben Sie ein Objekt mit der method-Eigenschaft auf "POST" gesetzt.
const response = await fetch("http://example.com", {
method: "POST",
body: "Hallo, Welt!",
});body kann ein String, ein FormData-Objekt, ein ArrayBuffer, ein Blob und mehr sein. Weitere Informationen finden Sie in der MDN-Dokumentation.
Proxying von Anfragen
Um eine Anfrage zu proxyen, übergeben Sie ein Objekt mit der proxy-Eigenschaft auf eine URL-Zeichenkette gesetzt:
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});Sie können auch ein Objektformat verwenden, um benutzerdefinierte Header an den Proxy-Server zu senden:
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer mein-token",
"X-Custom-Proxy-Header": "wert",
},
},
});Die headers werden direkt im CONNECT-Request (für HTTPS-Ziele) oder in der Proxy-Anfrage (für HTTP-Ziele) an den Proxy gesendet. Wenn Sie einen Proxy-Authorization-Header angeben, überschreibt dieser alle Anmeldeinformationen in der Proxy-URL.
Benutzerdefinierte Header
Um benutzerdefinierte Header zu setzen, übergeben Sie ein Objekt mit der headers-Eigenschaft auf ein Objekt gesetzt.
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "wert",
},
});Sie können Header auch mit dem Headers-Objekt setzen.
const headers = new Headers();
headers.append("X-Custom-Header", "wert");
const response = await fetch("http://example.com", {
headers,
});Response-Bodies
Um den Response-Body zu lesen, verwenden Sie eine der folgenden Methoden:
response.text(): Promise<string>: Gibt ein Promise zurück, das mit dem Response-Body als String aufgelöst wird.response.json(): Promise<any>: Gibt ein Promise zurück, das mit dem Response-Body als JSON-Objekt aufgelöst wird.response.formData(): Promise<FormData>: Gibt ein Promise zurück, das mit dem Response-Body alsFormData-Objekt aufgelöst wird.response.bytes(): Promise<Uint8Array>: Gibt ein Promise zurück, das mit dem Response-Body alsUint8Arrayaufgelöst wird.response.arrayBuffer(): Promise<ArrayBuffer>: Gibt ein Promise zurück, das mit dem Response-Body alsArrayBufferaufgelöst wird.response.blob(): Promise<Blob>: Gibt ein Promise zurück, das mit dem Response-Body alsBlobaufgelöst wird.
Streamen von Response-Bodies
Sie können Async-Iteratoren verwenden, um den Response-Body zu streamen.
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}Sie können auch direkter auf das ReadableStream-Objekt zugreifen.
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();Streamen von Request-Bodies
Sie können Daten in Request-Bodies auch mit einem ReadableStream streamen:
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hallo");
controller.enqueue(" ");
controller.enqueue("Welt");
controller.close();
},
});
const response = await fetch("http://example.com", {
method: "POST",
body: stream,
});Bei der Verwendung von Streams mit HTTP(S):
- Die Daten werden direkt ins Netzwerk gestreamt, ohne den gesamten Body im Speicher zu puffern
- Wenn die Verbindung unterbrochen wird, wird der Stream abgebrochen
- Der
Content-Length-Header wird nicht automatisch gesetzt, es sei denn, der Stream hat eine bekannte Größe
Bei der Verwendung von Streams mit S3:
- Für PUT/POST-Anfragen verwendet Bun automatisch Multipart-Upload
- Der Stream wird in Chunks konsumiert und parallel hochgeladen
- Der Fortschritt kann über die S3-Optionen überwacht werden
Abrufen einer URL mit Timeout
Um eine URL mit Timeout abzurufen, verwenden Sie AbortSignal.timeout:
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});Abbrechen einer Anfrage
Um eine Anfrage abzubrechen, verwenden Sie einen AbortController:
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();Unix-Domain-Sockets
Um eine URL mit einem Unix-Domain-Socket abzurufen, verwenden Sie die unix: string-Option:
const response = await fetch("https://hostname/a/path", {
unix: "/var/run/path/to/unix.sock",
method: "POST",
body: JSON.stringify({ message: "Hallo von Bun!" }),
headers: {
"Content-Type": "application/json",
},
});TLS
Um ein Client-Zertifikat zu verwenden, verwenden Sie die tls-Option:
await fetch("https://example.com", {
tls: {
key: Bun.file("/path/to/key.pem"),
cert: Bun.file("/path/to/cert.pem"),
// ca: [Bun.file("/path/to/ca.pem")],
},
});Benutzerdefinierte TLS-Validierung
Um die TLS-Validierung anzupassen, verwenden Sie die checkServerIdentity-Option in tls:
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// Geben Sie einen Error zurück, wenn das Zertifikat ungültig ist
},
},
});Dies ist ähnlich wie es im net-Modul von Node.js funktioniert.
Deaktivieren der TLS-Validierung
Um die TLS-Validierung zu deaktivieren, setzen Sie rejectUnauthorized auf false:
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});Dies ist besonders nützlich, um SSL-Fehler bei der Verwendung von selbstsignierten Zertifikaten zu vermeiden, aber dies deaktiviert die TLS-Validierung und sollte mit Vorsicht verwendet werden.
Anfrageoptionen
Zusätzlich zu den Standard-Fetch-Optionen bietet Bun mehrere Erweiterungen:
const response = await fetch("http://example.com", {
// Steuerung der automatischen Response-Dekomprimierung (Standard: true)
// Unterstützt gzip, deflate, brotli (br) und zstd
decompress: true,
// Deaktivieren der Wiederverwendung von Verbindungen für diese Anfrage
keepalive: false,
// Debug-Logging-Level
verbose: true, // oder "curl" für detailliertere Ausgabe
});Protokollunterstützung
Über HTTP(S) hinaus unterstützt Bun's Fetch mehrere zusätzliche Protokolle:
S3-URLs - s3://
Bun unterstützt das direkte Abrufen von S3-Buckets.
// Verwendung von Umgebungsvariablen für Anmeldeinformationen
const response = await fetch("s3://my-bucket/path/to/object");
// Oder explizites Übergeben von Anmeldeinformationen
const response = await fetch("s3://my-bucket/path/to/object", {
s3: {
accessKeyId: "IHR_ACCESS_KEY",
secretAccessKey: "IHR_SECRET_KEY",
region: "us-east-1",
},
});Hinweis: Nur PUT- und POST-Methoden unterstützen Request-Bodies bei der Verwendung von S3. Für Uploads verwendet Bun automatisch Multipart-Upload für streaming Bodies.
Weitere Informationen zur S3-Unterstützung von Bun finden Sie in der S3-Dokumentation.
Datei-URLs - file://
Sie können lokale Dateien mit dem file:-Protokoll abrufen:
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();Unter Windows werden Pfade automatisch normalisiert:
// Beide funktionieren unter Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");Data-URLs - data:
Bun unterstützt das data:-URL-Schema:
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hallo, Welt!"Blob-URLs - blob:
Sie können Blobs mit URLs abrufen, die von URL.createObjectURL() erstellt wurden:
const blob = new Blob(["Hallo, Welt!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);Fehlerbehandlung
Bun's Fetch-Implementierung umfasst mehrere spezifische Fehlerfälle:
- Die Verwendung eines Request-Bodys mit GET/HEAD-Methoden löst einen Fehler aus (was für die Fetch-API erwartet wird)
- Der Versuch, sowohl
proxy- als auchunix-Optionen zusammen zu verwenden, löst einen Fehler aus - TLS-Zertifikat-Validierungsfehler, wenn
rejectUnauthorizedtrue (oder undefined) ist - S3-Operationen können spezifische Fehler im Zusammenhang mit Authentifizierung oder Berechtigungen auslösen
Content-Type-Handling
Bun setzt automatisch den Content-Type-Header für Request-Bodies, wenn nicht explizit angegeben:
- Für
Blob-Objekte wird dertypedes Blobs verwendet - Für
FormDatawird die entsprechende Multipart-Grenze gesetzt
Debugging
Um beim Debugging zu helfen, können Sie verbose: true an fetch übergeben:
const response = await fetch("http://example.com", {
verbose: true,
});Dies gibt die Request- und Response-Header in Ihrem Terminal aus:
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
[fetch] < 200 OK
[fetch] < Content-Encoding: gzip
[fetch] < Age: 201555
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT
[fetch] < Etag: "3147526947+gzip"
[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: ECAcc (sac/254F)
[fetch] < Vary: Accept-Encoding
[fetch] < X-Cache: HIT
[fetch] < Content-Length: 648Hinweis: verbose: boolean ist nicht Teil der Web-Standard-fetch-API und ist spezifisch für Bun.
Performance
Bevor eine HTTP-Anfrage gesendet werden kann, muss der DNS-Lookup durchgeführt werden. Dies kann eine erhebliche Zeit in Anspruch nehmen, insbesondere wenn der DNS-Server langsam ist oder die Netzwerkverbindung schlecht ist.
Nach dem DNS-Lookup muss der TCP-Socket verbunden werden und der TLS-Handshake muss möglicherweise durchgeführt werden. Dies kann ebenfalls eine erhebliche Zeit in Anspruch nehmen.
Nachdem die Anfrage abgeschlossen ist, kann das Konsumieren des Response-Bodys ebenfalls eine erhebliche Zeit und Speicher in Anspruch nehmen.
Bei jedem Schritt bietet Bun APIs, die Ihnen helfen, die Performance Ihrer Anwendung zu optimieren.
DNS-Prefetching
Um einen DNS-Eintrag vorab zu laden, können Sie die dns.prefetch-API verwenden. Diese API ist nützlich, wenn Sie wissen, dass Sie bald eine Verbindung zu einem Host herstellen müssen und den anfänglichen DNS-Lookup vermeiden möchten.
import { dns } from "bun";
dns.prefetch("bun.com");DNS-Caching
Standardmäßig cached und dedupliziert Bun DNS-Abfragen im Speicher für bis zu 30 Sekunden. Sie können die Cache-Statistiken abrufen, indem Sie dns.getCacheStats() aufrufen:
Um mehr über DNS-Caching in Bun zu erfahren, lesen Sie die DNS-Caching-Dokumentation.
Vorverbindung zu einem Host
Um eine Vorverbindung zu einem Host herzustellen, können Sie die fetch.preconnect-API verwenden. Diese API ist nützlich, wenn Sie wissen, dass Sie bald eine Verbindung zu einem Host herstellen müssen und den anfänglichen DNS-Lookup, die TCP-Socket-Verbindung und den TLS-Handshake frühzeitig starten möchten.
import { fetch } from "bun";
fetch.preconnect("https://bun.com");Hinweis: Das sofortige Aufrufen von fetch nach fetch.preconnect macht Ihre Anfrage nicht schneller. Vorverbinden hilft nur, wenn Sie wissen, dass Sie bald eine Verbindung zu einem Host herstellen müssen, aber noch nicht bereit sind, die Anfrage zu stellen.
Vorverbindung beim Start
Um beim Start eine Vorverbindung zu einem Host herzustellen, können Sie --fetch-preconnect übergeben:
bun --fetch-preconnect https://bun.com ./my-script.tsDies ist ähnlich wie <link rel="preconnect"> in HTML.
Diese Funktion ist unter Windows noch nicht implementiert. Wenn Sie diese Funktion unter Windows verwenden möchten, erstellen Sie bitte ein Issue und wir können die Unterstützung dafür unter Windows implementieren.
Connection Pooling & HTTP Keep-Alive
Bun verwendet automatisch Verbindungen zum selben Host wieder. Dies wird als Connection Pooling bezeichnet. Dies kann die Zeit, die zum Herstellen einer Verbindung benötigt wird, erheblich reduzieren. Sie müssen nichts tun, um dies zu aktivieren; es ist automatisch.
Limit für gleichzeitige Verbindungen
Standardmäßig begrenzt Bun die maximale Anzahl gleichzeitiger fetch-Anfragen auf 256. Wir tun dies aus mehreren Gründen:
- Es verbessert die allgemeine Systemstabilität. Betriebssysteme haben eine Obergrenze für die Anzahl gleichzeitiger offener TCP-Sockets, normalerweise im niedrigen Tausenderbereich. Das Annähern an dieses Limit führt dazu, dass sich Ihr gesamter Computer seltsam verhält. Anwendungen hängen sich auf und stürzen ab.
- Es fördert die Wiederverwendung von HTTP Keep-Alive-Verbindungen. Für kurzlebige HTTP-Anfragen ist der langsamste Schritt oft das anfängliche Verbindungs-Setup. Die Wiederverwendung von Verbindungen kann viel Zeit sparen.
Wenn das Limit überschritten wird, werden die Anfragen in die Warteschlange gestellt und gesendet, sobald die nächste Anfrage endet.
Sie können die maximale Anzahl gleichzeitiger Verbindungen über die Umgebungsvariable BUN_CONFIG_MAX_HTTP_REQUESTS erhöhen:
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.tsDer Maximalwert für dieses Limit ist derzeit auf 65.336 gesetzt. Die maximale Portnummer ist 65.535, daher ist es für einen einzelnen Computer ziemlich schwierig, dieses Limit zu überschreiten.
Response-Pufferung
Bun unternimmt große Anstrengungen, um die Performance des Lesens des Response-Bodys zu optimieren. Der schnellste Weg, den Response-Body zu lesen, ist die Verwendung einer dieser Methoden:
response.text(): Promise<string>response.json(): Promise<any>response.formData(): Promise<FormData>response.bytes(): Promise<Uint8Array>response.arrayBuffer(): Promise<ArrayBuffer>response.blob(): Promise<Blob>
Sie können auch Bun.write verwenden, um den Response-Body in eine Datei auf der Festplatte zu schreiben:
import { write } from "bun";
await write("output.txt", response);Implementierungsdetails
- Connection Pooling ist standardmäßig aktiviert, kann aber pro Anfrage mit
keepalive: falsedeaktiviert werden. Der"Connection: close"-Header kann auch verwendet werden, um Keep-Alive zu deaktivieren. - Große Datei-Uploads werden unter bestimmten Bedingungen mit dem
sendfile-Syscall des Betriebssystems optimiert:- Die Datei muss größer als 32 KB sein
- Die Anfrage darf keinen Proxy verwenden
- Unter macOS können nur reguläre Dateien (keine Pipes, Sockets oder Geräte)
sendfileverwenden - Wenn diese Bedingungen nicht erfüllt sind oder wenn S3/Streaming-Uploads verwendet werden, fällt Bun auf das Lesen der Datei in den Speicher zurück
- Diese Optimierung ist besonders effektiv für HTTP- (nicht HTTPS-) Anfragen, bei denen die Datei direkt vom Kernel an den Netzwerk-Stack gesendet werden kann
- S3-Operationen verarbeiten automatisch das Signieren von Anfragen und das Zusammenführen von Authentifizierungs-Headern
Hinweis: Viele dieser Funktionen sind Bun-spezifische Erweiterungen der Standard-Fetch-API.