Skip to content

Bun реализует стандарт WHATWG fetch с некоторыми расширениями для удовлетворения потребностей серверного JavaScript.

Bun также реализует node:http но fetch обычно рекомендуется вместо него.

Отправка HTTP-запроса ​

Для отправки HTTP-запроса используйте fetch

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

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

const text = await response.text(); // или response.json(), response.formData() и т.д.

fetch также работает с HTTPS URL.

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

Вы также можете передать fetch объект Request.

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

const response = await fetch(request);

Отправка POST-запроса ​

Для отправки POST-запроса передайте объект со свойством method установленным в "POST".

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

body может быть строкой объектом FormData ArrayBuffer Blob и другим. См. документацию MDN для получения дополнительной информации.

Проксирование запросов ​

Для проксирования запроса передайте объект со свойством proxy установленным в строку URL:

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

Вы также можете использовать формат объекта для отправки пользовательских заголовков на прокси-сервер:

ts
const response = await fetch("http://example.com", {
  proxy: {
    url: "http://proxy.com",
    headers: {
      "Proxy-Authorization": "Bearer my-token",
      "X-Custom-Proxy-Header": "value",
    },
  },
});

headers отправляются непосредственно прокси в запросах CONNECT (для HTTPS-целей) или в запросе прокси (для HTTP-целей). Если вы предоставляете заголовок Proxy-Authorization он переопределяет любые учётные данные в URL прокси.

Пользовательские заголовки ​

Для установки пользовательских заголовков передайте объект со свойством headers установленным в объект.

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

Вы также можете установить заголовки используя объект Headers.

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

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

Тела ответов ​

Для чтения тела ответа используйте один из следующих методов:

  • response.text(): Promise<string>: Возвращает promise который разрешается с телом ответа как строка.
  • response.json(): Promise<any>: Возвращает promise который разрешается с телом ответа как JSON-объект.
  • response.formData(): Promise<FormData>: Возвращает promise который разрешается с телом ответа как объект FormData.
  • response.bytes(): Promise<Uint8Array>: Возвращает promise который разрешается с телом ответа как Uint8Array.
  • response.arrayBuffer(): Promise<ArrayBuffer>: Возвращает promise который разрешается с телом ответа как ArrayBuffer.
  • response.blob(): Promise<Blob>: Возвращает promise который разрешается с телом ответа как Blob.

Потоковые тела ответов ​

Вы можете использовать асинхронные итераторы для потоковой передачи тела ответа.

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

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

Вы также можете более непосредственно получить доступ к объекту ReadableStream.

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

const stream = response.body;

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

Потоковые тела запросов ​

Вы также можете потоково передавать данные в телах запросов используя 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,
});

При использовании потоков с HTTP(S):

  • Данные передаются непосредственно в сеть без буферизации всего тела в памяти
  • Если соединение потеряно поток будет отменён
  • Заголовок Content-Length не устанавливается автоматически если у потока нет известного размера

При использовании потоков с S3:

  • Для запросов PUT/POST Bun автоматически использует многокомпонентную загрузку
  • Поток потребляется по частям и загружается параллельно
  • Прогресс можно отслеживать через опции S3

Получение URL с таймаутом ​

Для получения URL с таймаутом используйте AbortSignal.timeout:

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

Отмена запроса ​

Для отмены запроса используйте AbortController:

ts
const controller = new AbortController();

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

controller.abort();

Сокеты доменов Unix ​

Для получения URL используя сокет домена Unix используйте опцию unix: string:

ts
const response = await fetch("https://hostname/a/path", {
  unix: "/var/run/path/to/unix.sock",
  method: "POST",
  body: JSON.stringify({ message: "Привет от Bun!" }),
  headers: {
    "Content-Type": "application/json",
  },
});

TLS ​

Для использования клиентского сертификата используйте опцию 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")],
  },
});

Пользовательская проверка TLS ​

Для настройки проверки TLS используйте опцию checkServerIdentity в tls

ts
await fetch("https://example.com", {
  tls: {
    checkServerIdentity: (hostname, peerCertificate) => {
      // Вернуть Error если сертификат недействителен
    },
  },
});

Это похоже на то как это работает в модуле net Node.

Отключение проверки TLS ​

Для отключения проверки TLS установите rejectUnauthorized в false:

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

Это особенно полезно для избежания ошибок SSL при использовании самоподписанных сертификатов но это отключает проверку TLS и должно использоваться с осторожностью.

Опции запроса ​

В дополнение к стандартным опциям fetch Bun предоставляет несколько расширений:

ts
const response = await fetch("http://example.com", {
  // Управление автоматической декомпрессией ответа (по умолчанию: true)
  // Поддерживает gzip, deflate, brotli (br) и zstd
  decompress: true,

  // Отключение повторного использования соединения для этого запроса
  keepalive: false,

  // Уровень отладочного логирования
  verbose: true, // или "curl" для более детального вывода
});

Поддержка протоколов ​

Помимо HTTP(S) fetch в Bun поддерживает несколько дополнительных протоколов:

URL S3 - s3:// ​

Bun поддерживает получение из S3-корзин непосредственно.

ts
// Использование переменных окружения для учётных данных
const response = await fetch("s3://my-bucket/path/to/object");

// Или передача учётных данных явно
const response = await fetch("s3://my-bucket/path/to/object", {
  s3: {
    accessKeyId: "YOUR_ACCESS_KEY",
    secretAccessKey: "YOUR_SECRET_KEY",
    region: "us-east-1",
  },
});

Примечание: Только методы PUT и POST поддерживают тела запросов при использовании S3. Для загрузок Bun автоматически использует многокомпонентную загрузку для потоковых тел.

Вы можете узнать больше о поддержке S3 в Bun в документации S3.

URL файлов - file:// ​

Вы можете получать локальные файлы используя протокол file::

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

В Windows пути автоматически нормализуются:

ts
// Оба работают в Windows
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");

URL данных - data: ​

Bun поддерживает схему URL data::

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

URL Blob - blob: ​

Вы можете получать blob используя URL созданные через URL.createObjectURL():

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

Обработка ошибок ​

Реализация fetch в Bun включает несколько специфичных случаев ошибок:

  • Использование тела запроса с методами GET/HEAD вызовет ошибку (что ожидается для API fetch)
  • Попытка использования обеих опций proxy и unix вместе вызовет ошибку
  • Ошибки проверки TLS-сертификата когда rejectUnauthorized true (или undefined)
  • Операции S3 могут вызывать специфичные ошибки связанные с аутентификацией или разрешениями

Обработка Content-Type ​

Bun автоматически устанавливает заголовок Content-Type для тел запросов когда не предоставлено явно:

  • Для объектов Blob использует type blob
  • Для FormData устанавливает соответствующую границу multipart

Отладка ​

Для помощи в отладке вы можете передать verbose: true в fetch:

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

Это выведет заголовки запроса и ответа в ваш терминал:

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

Примечание: verbose: boolean не является частью веб-стандарта fetch API и специфично для Bun.

Производительность ​

Перед отправкой HTTP-запроса должна быть выполнена DNS-lookup. Это может занять значительное время особенно если DNS-сервер медленный или сетевое соединение плохое.

После DNS-lookup TCP-сокет должен быть подключён и может потребоваться выполнение TLS-рукопожатия. Это также может занять значительное время.

После завершения запроса потребление тела ответа также может занять значительное время и память.

На каждом этапе Bun предоставляет API для помощи в оптимизации производительности вашего приложения.

Предварительное получение DNS ​

Для предварительного получения DNS-записи вы можете использовать API dns.prefetch. Этот API полезен когда вы знаете что вам скоро нужно будет подключиться к хосту и хотите избежать начального DNS-запроса.

ts
import { dns } from "bun";

dns.prefetch("bun.com");

Кэширование DNS ​

По умолчанию Bun кэширует и дублирует DNS-запросы в памяти до 30 секунд. Вы можете увидеть статистику кэша вызвав dns.getCacheStats():

Чтобы узнать больше о кэшировании DNS в Bun см. документацию Кэширование DNS.

Предварительное подключение к хосту ​

Для предварительного подключения к хосту вы можете использовать API fetch.preconnect. Этот API полезен когда вы знаете что вам скоро нужно будет подключиться к хосту и хотите начать начальный DNS-lookup подключение TCP-сокета и TLS-рукопожатие заранее.

ts
import { fetch } from "bun";

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

Примечание: вызов fetch сразу после fetch.preconnect не сделает ваш запрос быстрее. Предварительное подключение помогает только если вы знаете что вам скоро нужно будет подключиться к хосту но вы ещё не готовы сделать запрос.

Предварительное подключение при запуске ​

Для предварительного подключения к хосту при запуске вы можете передать --fetch-preconnect:

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

Это похоже на <link rel="preconnect"> в HTML.

Эта функция ещё не реализована в Windows. Если вы заинтересованы в использовании этой функции в Windows пожалуйста создайте issue и мы можем реализовать поддержку для неё в Windows.

Объединение соединений и HTTP keep-alive ​

Bun автоматически повторно использует соединения с одним хостом. Это известно как объединение соединений. Это может значительно уменьшить время необходимое для установления соединения. Вам не нужно ничего делать для включения этого; это автоматически.

Лимит одновременных соединений ​

По умолчанию Bun ограничивает максимальное количество одновременных запросов fetch до 256. Мы делаем это по нескольким причинам:

  • Это улучшает общую стабильность системы. Операционные системы имеют верхний лимит на количество одновременных открытых TCP-сокетов обычно в низких тысячах. Приближение к этому лимиту заставляет весь ваш компьютер вести себя странно. Приложения зависают и падают.
  • Это поощряет повторное использование соединений HTTP Keep-Alive. Для коротких HTTP-запросов самым медленным шагом часто является начальная настройка соединения. Повторное использование соединений может сэкономить много времени.

Когда лимит превышен запросы ставятся в очередь и отправляются как только следующий запрос завершается.

Вы можете увеличить максимальное количество одновременных соединений через переменную окружения BUN_CONFIG_MAX_HTTP_REQUESTS:

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

Максимальное значение для этого лимита в настоящее время установлено в 65,336. Максимальный номер порта 65,535 поэтому довольно трудно любому одному компьютеру превысить этот лимит.

Буферизация ответов ​

Bun прилагает большие усилия для оптимизации производительности чтения тела ответа. Самый быстрый способ прочитать тело ответа — использовать один из этих методов:

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

Вы также можете использовать Bun.write для записи тела ответа в файл на диске:

ts
import { write } from "bun";

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

Детали реализации ​

  • Объединение соединений включено по умолчанию но может быть отключено для каждого запроса с keepalive: false. Заголовок "Connection: close" также может использоваться для отключения keep-alive.
  • Загрузки больших файлов оптимизированы используя системный вызов sendfile операционной системы при определённых условиях:
    • Файл должен быть больше 32KB
    • Запрос не должен использовать прокси
    • На macOS только обычные файлы (не трубы сокеты или устройства) могут использовать sendfile
    • Когда эти условия не выполнены или при использовании S3/потоковых загрузок Bun возвращается к чтению файла в память
    • Эта оптимизация особенно эффективна для HTTP (не HTTPS) запросов где файл может быть отправлен непосредственно из ядра в сетевой стек
  • Операции S3 автоматически обрабатывают подписание запросов и слияние заголовков аутентификации

Примечание: Многие из этих функций являются специфичными для Bun расширениями стандартного API fetch.

Bun от www.bunjs.com.cn