Bun implémente le standard WHATWG fetch, avec quelques extensions pour répondre aux besoins du JavaScript côté serveur.
Bun implémente également node:http, mais fetch est généralement recommandé à la place.
Envoyer une requête HTTP
Pour envoyer une requête HTTP, utilisez fetch
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // ou response.json(), response.formData(), etc.fetch fonctionne également avec les URL HTTPS.
const response = await fetch("https://example.com");Vous pouvez également passer à fetch un objet Request.
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);Envoyer une requête POST
Pour envoyer une requête POST, passez un objet avec la propriété method définie sur "POST".
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});body peut être une chaîne, un objet FormData, un ArrayBuffer, un Blob, et plus encore. Consultez la documentation MDN pour plus d'informations.
Proxyfier des requêtes
Pour proxyfier une requête, passez un objet avec la propriété proxy définie sur une URL :
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});Vous pouvez également utiliser un format d'objet pour envoyer des en-têtes personnalisés au serveur proxy :
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});Les headers sont envoyés directement au proxy dans les requêtes CONNECT (pour les cibles HTTPS) ou dans la requête proxy (pour les cibles HTTP). Si vous fournissez un en-tête Proxy-Authorization, il remplace toutes les informations d'identification dans l'URL du proxy.
En-têtes personnalisés
Pour définir des en-têtes personnalisés, passez un objet avec la propriété headers définie sur un objet.
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});Vous pouvez également définir des en-têtes en utilisant l'objet Headers.
const headers = new Headers();
headers.append("X-Custom-Header", "value");
const response = await fetch("http://example.com", {
headers,
});Corps de réponse
Pour lire le corps de la réponse, utilisez l'une des méthodes suivantes :
response.text(): Promise<string>: Renvoie une promesse qui se résout avec le corps de la réponse sous forme de chaîne.response.json(): Promise<any>: Renvoie une promesse qui se résout avec le corps de la réponse sous forme d'objet JSON.response.formData(): Promise<FormData>: Renvoie une promesse qui se résout avec le corps de la réponse sous forme d'objetFormData.response.bytes(): Promise<Uint8Array>: Renvoie une promesse qui se résout avec le corps de la réponse sous forme deUint8Array.response.arrayBuffer(): Promise<ArrayBuffer>: Renvoie une promesse qui se résout avec le corps de la réponse sous forme deArrayBuffer.response.blob(): Promise<Blob>: Renvoie une promesse qui se résout avec le corps de la réponse sous forme deBlob.
Corps de réponse en streaming
Vous pouvez utiliser des itérateurs asynchrones pour diffuser le corps de la réponse.
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}Vous pouvez également accéder plus directement à l'objet ReadableStream.
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();Corps de requête en streaming
Vous pouvez également diffuser des données dans les corps de requête en utilisant un ReadableStream :
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello");
controller.enqueue(" ");
controller.enqueue("World");
controller.close();
},
});
const response = await fetch("http://example.com", {
method: "POST",
body: stream,
});Lors de l'utilisation de streams avec HTTP(S) :
- Les données sont diffusées directement vers le réseau sans mettre en mémoire tampon l'intégralité du corps
- Si la connexion est perdue, le stream sera annulé
- L'en-tête
Content-Lengthn'est pas automatiquement défini sauf si le stream a une taille connue
Lors de l'utilisation de streams avec S3 :
- Pour les requêtes PUT/POST, Bun utilise automatiquement le téléchargement multipart
- Le stream est consommé par morceaux et téléchargé en parallèle
- La progression peut être surveillée via les options S3
Fetch d'une URL avec un délai d'attente
Pour fetcher une URL avec un délai d'attente, utilisez AbortSignal.timeout :
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});Annuler une requête
Pour annuler une requête, utilisez un AbortController :
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();Sockets de domaine Unix
Pour fetcher une URL en utilisant un socket de domaine Unix, utilisez l'option unix: string :
const response = await fetch("https://hostname/a/path", {
unix: "/var/run/path/to/unix.sock",
method: "POST",
body: JSON.stringify({ message: "Hello from Bun!" }),
headers: {
"Content-Type": "application/json",
},
});TLS
Pour utiliser un certificat client, utilisez l'option tls :
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")],
},
});Validation TLS personnalisée
Pour personnaliser la validation TLS, utilisez l'option checkServerIdentity dans tls
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// Renvoie une Error si le certificat est invalide
},
},
});C'est similaire à la façon dont cela fonctionne dans le module net de Node.
Désactiver la validation TLS
Pour désactiver la validation TLS, définissez rejectUnauthorized sur false :
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});Ceci est particulièrement utile pour éviter les erreurs SSL lors de l'utilisation de certificats auto-signés, mais cela désactive la validation TLS et doit être utilisé avec prudence.
Options de requête
En plus des options fetch standard, Bun fournit plusieurs extensions :
const response = await fetch("http://example.com", {
// Contrôle la décompression automatique de la réponse (par défaut : true)
// Prend en charge gzip, deflate, brotli (br), et zstd
decompress: true,
// Désactiver la réutilisation de connexion pour cette requête
keepalive: false,
// Niveau de journalisation de débogage
verbose: true, // ou "curl" pour une sortie plus détaillée
});Prise en charge des protocoles
Au-delà de HTTP(S), le fetch de Bun prend en charge plusieurs protocoles supplémentaires :
URL S3 - s3://
Bun prend en charge le fetch depuis des buckets S3 directement.
// Utilisation des variables d'environnement pour les informations d'identification
const response = await fetch("s3://my-bucket/path/to/object");
// Ou passage explicite des informations d'identification
const response = await fetch("s3://my-bucket/path/to/object", {
s3: {
accessKeyId: "YOUR_ACCESS_KEY",
secretAccessKey: "YOUR_SECRET_KEY",
region: "us-east-1",
},
});Note : Seules les méthodes PUT et POST prennent en charge les corps de requête lors de l'utilisation de S3. Pour les téléchargements, Bun utilise automatiquement le téléchargement multipart pour les corps en streaming.
Vous pouvez en savoir plus sur la prise en charge S3 de Bun dans la documentation S3.
URL de fichier - file://
Vous pouvez fetcher des fichiers locaux en utilisant le protocole file: :
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();Sur Windows, les chemins sont automatiquement normalisés :
// Les deux fonctionnent sur Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");URL de données - data:
Bun prend en charge le schéma d'URL data: :
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"URL Blob - blob:
Vous pouvez fetcher des blobs en utilisant des URL créées par URL.createObjectURL() :
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);Gestion des erreurs
L'implémentation fetch de Bun inclut plusieurs cas d'erreur spécifiques :
- L'utilisation d'un corps de requête avec les méthodes GET/HEAD déclenchera une erreur (ce qui est attendu pour l'API fetch)
- Tenter d'utiliser les options
proxyetunixensemble déclenchera une erreur - Les échecs de validation de certificat TLS lorsque
rejectUnauthorizedest true (ou undefined) - Les opérations S3 peuvent déclencher des erreurs spécifiques liées à l'authentification ou aux autorisations
Gestion de Content-Type
Bun définit automatiquement l'en-tête Content-Type pour les corps de requête lorsqu'il n'est pas explicitement fourni :
- Pour les objets
Blob, utilise letypedu blob - Pour
FormData, définit la limite multipart appropriée
Débogage
Pour aider au débogage, vous pouvez passer verbose: true à fetch :
const response = await fetch("http://example.com", {
verbose: true,
});Cela affichera les en-têtes de requête et de réponse dans votre terminal :
[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: 648Note : verbose: boolean ne fait pas partie de l'API fetch standard Web et est spécifique à Bun.
Performance
Avant qu'une requête HTTP puisse être envoyée, la recherche DNS doit être effectuée. Cela peut prendre un temps significatif, surtout si le serveur DNS est lent ou si la connexion réseau est mauvaise.
Après la recherche DNS, le socket TCP doit être connecté et la poignée de main TLS peut devoir être effectuée. Cela peut également prendre un temps significatif.
Après que la requête soit terminée, la consommation du corps de la réponse peut également prendre un temps et une mémoire significatifs.
À chaque étape, Bun fournit des API pour vous aider à optimiser les performances de votre application.
Préchargement DNS
Pour précharger une entrée DNS, vous pouvez utiliser l'API dns.prefetch. Cette API est utile lorsque vous savez que vous devrez vous connecter à un hôte bientôt et que vous souhaitez éviter la recherche DNS initiale.
import { dns } from "bun";
dns.prefetch("bun.com");Mise en cache DNS
Par défaut, Bun met en cache et déduplique les requêtes DNS en mémoire pendant un maximum de 30 secondes. Vous pouvez voir les statistiques du cache en appelant dns.getCacheStats() :
Pour en savoir plus sur la mise en cache DNS dans Bun, consultez la documentation Mise en cache DNS.
Préconnexion à un hôte
Pour préconnecter à un hôte, vous pouvez utiliser l'API fetch.preconnect. Cette API est utile lorsque vous savez que vous devrez vous connecter à un hôte bientôt et que vous souhaitez démarrer la recherche DNS initiale, la connexion du socket TCP et la poignée de main TLS tôt.
import { fetch } from "bun";
fetch.preconnect("https://bun.com");Note : appeler fetch immédiatement après fetch.preconnect ne rendra pas votre requête plus rapide. La préconnexion aide seulement si vous savez que vous devrez vous connecter à un hôte bientôt, mais que vous n'êtes pas encore prêt à faire la requête.
Préconnexion au démarrage
Pour préconnecter à un hôte au démarrage, vous pouvez passer --fetch-preconnect :
bun --fetch-preconnect https://bun.com ./my-script.tsC'est en quelque sorte comme <link rel="preconnect"> en HTML.
Cette fonctionnalité n'est pas encore implémentée sur Windows. Si vous êtes intéressé par l'utilisation de cette fonctionnalité sur Windows, veuillez ouvrir un problème et nous pouvons implémenter la prise en charge pour Windows.
Mise en pool des connexions et keep-alive HTTP
Bun réutilise automatiquement les connexions au même hôte. C'est ce qu'on appelle la mise en pool des connexions. Cela peut réduire considérablement le temps nécessaire pour établir une connexion. Vous n'avez rien à faire pour activer cela ; c'est automatique.
Limite de connexions simultanées
Par défaut, Bun limite le nombre maximum de requêtes fetch simultanées à 256. Nous faisons cela pour plusieurs raisons :
- Cela améliore la stabilité globale du système. Les systèmes d'exploitation ont une limite supérieure sur le nombre de sockets TCP ouverts simultanés, généralement dans les bas milliers. Approcher cette limite fait que votre ordinateur entier se comporte étrangement. Les applications se bloquent et plantent.
- Cela encourage la réutilisation des connexions HTTP Keep-Alive. Pour les requêtes HTTP de courte durée, l'étape la plus lente est souvent la configuration initiale de la connexion. Réutiliser les connexions peut faire gagner beaucoup de temps.
Lorsque la limite est dépassée, les requêtes sont mises en file d'attente et envoyées dès que la requête suivante se termine.
Vous pouvez augmenter le nombre maximum de connexions simultanées via la variable d'environnement BUN_CONFIG_MAX_HTTP_REQUESTS :
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.tsLa valeur maximale pour cette limite est actuellement définie à 65 336. Le numéro de port maximum est 65 535, il est donc très difficile pour un seul ordinateur de dépasser cette limite.
Mise en tampon de la réponse
Bun fait tout son possible pour optimiser les performances de lecture du corps de la réponse. La façon la plus rapide de lire le corps de la réponse est d'utiliser l'une de ces méthodes :
response.text(): Promise<string>response.json(): Promise<any>response.formData(): Promise<FormData>response.bytes(): Promise<Uint8Array>response.arrayBuffer(): Promise<ArrayBuffer>response.blob(): Promise<Blob>
Vous pouvez également utiliser Bun.write pour écrire le corps de la réponse dans un fichier sur le disque :
import { write } from "bun";
await write("output.txt", response);Détails d'implémentation
- La mise en pool des connexions est activée par défaut mais peut être désactivée par requête avec
keepalive: false. L'en-tête"Connection: close"peut également être utilisé pour désactiver le keep-alive. - Les téléchargements de fichiers volumineux sont optimisés en utilisant l'appel système
sendfiledu système d'exploitation dans des conditions spécifiques :- Le fichier doit être plus grand que 32 Ko
- La requête ne doit pas utiliser de proxy
- Sur macOS, seuls les fichiers réguliers (pas les pipes, sockets ou périphériques) peuvent utiliser
sendfile - Lorsque ces conditions ne sont pas remplies, ou lors de l'utilisation de téléchargements S3/streaming, Bun revient à la lecture du fichier en mémoire
- Cette optimisation est particulièrement efficace pour les requêtes HTTP (pas HTTPS) où le fichier peut être envoyé directement du noyau à la pile réseau
- Les opérations S3 gèrent automatiquement la signature des requêtes et la fusion des en-têtes d'authentification
Note : Beaucoup de ces fonctionnalités sont des extensions spécifiques à Bun de l'API fetch standard.