Skip to content

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

ts
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.

ts
const response = await fetch("https://example.com");

Vous pouvez également passer à fetch un objet Request.

ts
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".

ts
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 :

ts
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 :

ts
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.

ts
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.

ts
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'objet FormData.
  • response.bytes(): Promise<Uint8Array> : Renvoie une promesse qui se résout avec le corps de la réponse sous forme de Uint8Array.
  • response.arrayBuffer(): Promise<ArrayBuffer> : Renvoie une promesse qui se résout avec le corps de la réponse sous forme de ArrayBuffer.
  • response.blob(): Promise<Blob> : Renvoie une promesse qui se résout avec le corps de la réponse sous forme de Blob.

Corps de réponse en streaming

Vous pouvez utiliser des itérateurs asynchrones pour diffuser le corps de la réponse.

ts
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.

ts
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 :

ts
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-Length n'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 :

ts
const response = await fetch("http://example.com", {
  signal: AbortSignal.timeout(1000),
});

Annuler une requête

Pour annuler une requête, utilisez un AbortController :

ts
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 :

ts
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 :

ts
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

ts
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 :

ts
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 :

ts
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.

ts
// 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: :

ts
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();

Sur Windows, les chemins sont automatiquement normalisés :

ts
// 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: :

ts
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() :

ts
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 proxy et unix ensemble déclenchera une erreur
  • Les échecs de validation de certificat TLS lorsque rejectUnauthorized est 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 le type du blob
  • Pour FormData, définit la limite multipart appropriée

Débogage

Pour aider au débogage, vous pouvez passer verbose: true à fetch :

ts
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 :

sh
[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: 648

Note : 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.

ts
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.

ts
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 :

sh
bun --fetch-preconnect https://bun.com ./my-script.ts

C'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 :

sh
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts

La 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 :

ts
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 sendfile du 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.

Bun édité par www.bunjs.com.cn