Skip to content

Bun implementa lo standard WHATWG fetch, con alcune estensioni per soddisfare le esigenze del JavaScript lato server.

Bun implementa anche node:http, ma fetch è generalmente consigliato invece.

Inviare una richiesta HTTP

Per inviare una richiesta HTTP, usa fetch

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

console.log(response.status); // => 200

const text = await response.text(); // oppure response.json(), response.formData(), ecc.

fetch funziona anche con URL HTTPS.

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

Puoi anche passare a fetch un oggetto Request.

ts
const request = new Request("http://example.com", {
  method: "POST",
  body: "Hello, world!",
});

const response = await fetch(request);

Inviare una richiesta POST

Per inviare una richiesta POST, passa un oggetto con la proprietà method impostata a "POST".

ts
const response = await fetch("http://example.com", {
  method: "POST",
  body: "Hello, world!",
});

body può essere una stringa, un oggetto FormData, un ArrayBuffer, un Blob e altro. Consulta la documentazione MDN per maggiori informazioni.

Proxy delle richieste

Per fare proxy di una richiesta, passa un oggetto con la proprietà proxy impostata a una stringa URL:

ts
const response = await fetch("http://example.com", {
  proxy: "http://proxy.com",
});

Puoi anche usare un formato oggetto per inviare header personalizzati al server 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",
    },
  },
});

Gli headers vengono inviati direttamente al proxy nelle richieste CONNECT (per target HTTPS) o nella richiesta proxy (per target HTTP). Se fornisci un header Proxy-Authorization, questo sovrascrive qualsiasi credenziale nell'URL del proxy.

Header personalizzati

Per impostare header personalizzati, passa un oggetto con la proprietà headers impostata a un oggetto.

ts
const response = await fetch("http://example.com", {
  headers: {
    "X-Custom-Header": "value",
  },
});

Puoi anche impostare header usando l'oggetto Headers.

ts
const headers = new Headers();
headers.append("X-Custom-Header", "value");

const response = await fetch("http://example.com", {
  headers,
});

Corpi delle risposte

Per leggere il corpo della risposta, usa uno dei seguenti metodi:

  • response.text(): Promise<string>: Restituisce una promise che si risolve con il corpo della risposta come stringa.
  • response.json(): Promise<any>: Restituisce una promise che si risolve con il corpo della risposta come oggetto JSON.
  • response.formData(): Promise<FormData>: Restituisce una promise che si risolve con il corpo della risposta come oggetto FormData.
  • response.bytes(): Promise<Uint8Array>: Restituisce una promise che si risolve con il corpo della risposta come Uint8Array.
  • response.arrayBuffer(): Promise<ArrayBuffer>: Restituisce una promise che si risolve con il corpo della risposta come ArrayBuffer.
  • response.blob(): Promise<Blob>: Restituisce una promise che si risolve con il corpo della risposta come Blob.

Streaming dei corpi delle risposte

Puoi usare iteratori asincroni per fare streaming del corpo della risposta.

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

for await (const chunk of response.body) {
  console.log(chunk);
}

Puoi anche accedere più direttamente all'oggetto ReadableStream.

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

const stream = response.body;

const reader = stream.getReader();
const { value, done } = await reader.read();

Streaming dei corpi delle richieste

Puoi anche trasmettere in streaming i dati nei corpi delle richieste usando 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,
});

Quando usi stream con HTTP(S):

  • I dati vengono trasmessi direttamente in rete senza memorizzare l'intero corpo in memoria
  • Se la connessione viene persa, lo stream sarà cancellato
  • L'header Content-Length non viene impostato automaticamente a meno che lo stream abbia una dimensione nota

Quando usi stream con S3:

  • Per richieste PUT/POST, Bun usa automaticamente upload multipart
  • Lo stream viene consumato in chunk e caricato in parallelo
  • Il progresso può essere monitorato attraverso le opzioni S3

Recuperare un URL con timeout

Per recuperare un URL con timeout, usa AbortSignal.timeout:

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

Annullare una richiesta

Per annullare una richiesta, usa un AbortController:

ts
const controller = new AbortController();

const response = await fetch("http://example.com", {
  signal: controller.signal,
});

controller.abort();

Socket di dominio Unix

Per recuperare un URL usando un socket di dominio Unix, usa l'opzione 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

Per usare un certificato client, usa l'opzione 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")],
  },
});

Validazione TLS personalizzata

Per personalizzare la validazione TLS, usa l'opzione checkServerIdentity in tls

ts
await fetch("https://example.com", {
  tls: {
    checkServerIdentity: (hostname, peerCertificate) => {
      // Restituisci un Error se il certificato non è valido
    },
  },
});

Questo è simile a come funziona nel modulo net di Node.

Disabilitare la validazione TLS

Per disabilitare la validazione TLS, imposta rejectUnauthorized a false:

ts
await fetch("https://example.com", {
  tls: {
    rejectUnauthorized: false,
  },
});

Questo è particolarmente utile per evitare errori SSL quando si usano certificati autofirmati, ma questo disabilita la validazione TLS e dovrebbe essere usato con cautela.

Opzioni della richiesta

Oltre alle opzioni standard di fetch, Bun fornisce diverse estensioni:

ts
const response = await fetch("http://example.com", {
  // Controlla la decompressione automatica della risposta (default: true)
  // Supporta gzip, deflate, brotli (br) e zstd
  discomprime: true,

  // Disabilita il riutilizzo della connessione per questa richiesta
  keepalive: false,

  // Livello di log di debug
  verbose: true, // oppure "curl" per output più dettagliato
});

Supporto del protocollo

Oltre a HTTP(S), il fetch di Bun supporta diversi protocolli aggiuntivi:

URL S3 - s3://

Bun supporta il recupero da bucket S3 direttamente.

ts
// Usando variabili d'ambiente per le credenziali
const response = await fetch("s3://my-bucket/path/to/object");

// O passano credenziali esplicitamente
const response = await fetch("s3://my-bucket/path/to/object", {
  s3: {
    accessKeyId: "YOUR_ACCESS_KEY",
    secretAccessKey: "YOUR_SECRET_KEY",
    region: "us-east-1",
  },
});

Nota: Solo i metodi PUT e POST supportano i corpi delle richieste quando si usa S3. Per i caricamenti, Bun usa automaticamente upload multipart per gli stream body.

Puoi leggere di più sul supporto S3 di Bun nella documentazione S3.

URL file - file://

Puoi recuperare file locali usando il protocollo file::

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

Su Windows, i percorsi vengono normalizzati automaticamente:

ts
// Entrambi funzionano su Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");

URL data - data:

Bun supporta lo schema URL data::

ts
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"

URL blob - blob:

Puoi recuperare blob usando URL creati da URL.createObjectURL():

ts
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);

Gestione degli errori

L'implementazione di fetch di Bun include diversi casi di errore specifici:

  • L'uso di un corpo della richiesta con metodi GET/HEAD genererà un errore (che è previsto per l'API fetch)
  • Il tentativo di usare sia le opzioni proxy che unix insieme genererà un errore
  • Fallimenti della validazione del certificato TLS quando rejectUnauthorized è true (o non definito)
  • Le operazioni S3 possono generare errori specifici relativi all'autenticazione o ai permessi

Gestione del Content-Type

Bun imposta automaticamente l'header Content-Type per i corpi delle richieste quando non esplicitamente fornito:

  • Per oggetti Blob, usa il type del blob
  • Per FormData, imposta il boundary multipart appropriato

Debug

Per aiutare con il debug, puoi passare verbose: true a fetch:

ts
const response = await fetch("http://example.com", {
  verbose: true,
});

Questo stamperà gli header della richiesta e della risposta sul tuo terminale:

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

Nota: verbose: boolean non fa parte dello standard web dell'API fetch ed è specifico di Bun.

Prestazioni

Prima che una richiesta HTTP possa essere inviata, deve essere eseguita la ricerca DNS. Questo può richiedere un tempo significativo, specialmente se il server DNS è lento o la connessione di rete è scarsa.

Dopo la ricerca DNS, il socket TCP deve essere connesso e l'handshake TLS potrebbe dover essere eseguito. Anche questo può richiedere un tempo significativo.

Dopo che la richiesta è completata, consumare il corpo della risposta può richiedere anche un tempo significativo e memoria.

In ogni fase, Bun fornisce API per aiutarti a ottimizzare le prestazioni della tua applicazione.

Prefetch DNS

Per prefetchare una voce DNS, puoi usare l'API dns.prefetch. Questa API è utile quando sai che dovrai connetterti a un host presto e vuoi evitare la ricerca DNS iniziale.

ts
import { dns } from "bun";

dns.prefetch("bun.com");

Cache DNS

Di default, Bun memorizza nella cache e deduplica le query DNS in memoria per fino a 30 secondi. Puoi vedere le statistiche della cache chiamando dns.getCacheStats():

Per saperne di più sulla cache DNS in Bun, consulta la documentazione sulla cache DNS.

Preconnettersi a un host

Per preconnetterti a un host, puoi usare l'API fetch.preconnect. Questa API è utile quando sai che dovrai connetterti a un host presto e vuoi iniziare la ricerca DNS iniziale, la connessione del socket TCP e l'handshake TLS presto.

ts
import { fetch } from "bun";

fetch.preconnect("https://bun.com");

Nota: chiamare fetch immediatamente dopo fetch.preconnect non renderà la tua richiesta più veloce. La preconnessione aiuta solo se sai che dovrai connetterti a un host presto, ma non sei ancora pronto per fare la richiesta.

Preconnessione all'avvio

Per preconnetterti a un host all'avvio, puoi passare --fetch-preconnect:

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

Questo è un po' come <link rel="preconnect"> in HTML.

Questa funzionalità non è ancora implementata su Windows. Se sei interessato a usare questa funzionalità su Windows, per favore apri un issue e possiamo implementare il supporto per essa su Windows.

Connection pooling e HTTP keep-alive

Bun riutilizza automaticamente le connessioni allo stesso host. Questo è noto come connection pooling. Questo può ridurre significativamente il tempo necessario per stabilire una connessione. Non devi fare nulla per abilitare questo; è automatico.

Limite di connessioni simultanee

Di default, Bun limita il numero massimo di richieste fetch simultanee a 256. Lo facciamo per diversi motivi:

  • Migliora la stabilità generale del sistema. I sistemi operativi hanno un limite superiore sul numero di socket TCP aperti simultaneamente, di solito nelle migliaia. Avvicinarsi a questo limite fa comportare in modo strano l'intero computer. Le applicazioni si bloccano e crashano.
  • Incoraggia il riutilizzo delle connessioni HTTP Keep-Alive. Per richieste HTTP di breve durata, il passaggio più lento è spesso la configurazione iniziale della connessione. Riutilizzare le connessioni può far risparmiare molto tempo.

Quando il limite viene superato, le richieste vengono accodate e inviate non appena termina la prossima richiesta.

Puoi aumentare il numero massimo di connessioni simultanee tramite la variabile d'ambiente BUN_CONFIG_MAX_HTTP_REQUESTS:

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

Il valore massimo per questo limite è attualmente impostato a 65.336. Il numero di porta massimo è 65.535, quindi è abbastanza difficile per qualsiasi computer superare questo limite.

Buffering della risposta

Bun fa di tutto per ottimizzare le prestazioni della lettura del corpo della risposta. Il modo più veloce per leggere il corpo della risposta è usare uno di questi metodi:

  • response.text(): Promise<string>
  • response.json(): Promise<any>
  • response.formData(): Promise<FormData>
  • response.bytes(): Promise<Uint8Array>
  • response.arrayBuffer(): Promise<ArrayBuffer>
  • response.blob(): Promise<Blob>

Puoi anche usare Bun.write per scrivere il corpo della risposta su un file su disco:

ts
import { write } from "bun";

await write("output.txt", response);

Dettagli dell'implementazione

  • Il connection pooling è abilitato di default ma può essere disabilitato per richiesta con keepalive: false. L'header "Connection: close" può anche essere usato per disabilitare keep-alive.
  • I caricamenti di file grandi sono ottimizzati usando la syscall sendfile del sistema operativo sotto condizioni specifiche:
    • Il file deve essere più grande di 32KB
    • La richiesta non deve usare un proxy
    • Su macOS, solo file regolari (non pipe, socket o dispositivi) possono usare sendfile
    • Quando queste condizioni non sono soddisfatte, o quando si usano caricamenti S3/streaming, Bun legge il file in memoria
    • Questa ottimizzazione è particolarmente efficace per richieste HTTP (non HTTPS) dove il file può essere inviato direttamente dal kernel allo stack di rete
  • Le operazioni S3 gestiscono automaticamente la firma delle richieste e l'unione degli header di autenticazione

Nota: Molte di queste funzionalità sono estensioni specifiche di Bun all'API fetch standard.

Bun a cura di www.bunjs.com.cn