Bun は WHATWG fetch 標準を実装しており、サーバーサイド JavaScript のニーズを満たすためのいくつかの拡張があります。
Bun は node:http も実装していますが、代わりに fetch を使用することをお勧めします。
HTTP リクエストの送信
HTTP リクエストを送信するには、fetch を使用します。
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // または response.json()、response.formData() などfetch は HTTPS URL でも動作します。
const response = await fetch("https://example.com");fetch に Request オブジェクトを渡すこともできます。
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);POST リクエストの送信
POST リクエストを送信するには、method プロパティを "POST" に設定したオブジェクトを渡します。
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});body は文字列、FormData オブジェクト、ArrayBuffer、Blob などになります。詳細は MDN ドキュメント を参照してください。
リクエストのプロキシ
リクエストをプロキシするには、proxy プロパティを URL 文字列に設定したオブジェクトを渡します。
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});プロキシサーバーにカスタムヘッダーを送信するためにオブジェクト形式を使用することもできます。
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});headers は、HTTPS ターゲットの CONNECT リクエストまたは HTTP ターゲットのプロキシリクエストでプロキシに直接送信されます。Proxy-Authorization ヘッダーを提供すると、プロキシ URL の認証情報が上書きされます。
カスタムヘッダー
カスタムヘッダーを設定するには、headers プロパティをオブジェクトに設定したオブジェクトを渡します。
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});Headers オブジェクトを使用してヘッダーを設定することもできます。
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>: レスポンスボディを JSON オブジェクトとして解決する Promise を返しますresponse.formData(): Promise<FormData>: レスポンスボディをFormDataオブジェクトとして解決する Promise を返しますresponse.bytes(): Promise<Uint8Array>: レスポンスボディをUint8Arrayとして解決する Promise を返しますresponse.arrayBuffer(): Promise<ArrayBuffer>: レスポンスボディをArrayBufferとして解決する Promise を返しますresponse.blob(): Promise<Blob>: レスポンスボディをBlobとして解決する Promise を返します
レスポンスボディのストリーミング
非同期イテレーターを使用してレスポンスボディをストリーミングできます。
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}ReadableStream オブジェクトに直接アクセスすることもできます。
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();リクエストボディのストリーミング
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,
});HTTP(S) でストリームを使用する場合:
- データはメモリにボディ全体をバッファリングせずにネットワークに直接ストリーミングされます
- 接続が失われた場合、ストリームはキャンセルされます
- ストリームのサイズが既知でない限り、
Content-Lengthヘッダーは自動的に設定されません
S3 でストリームを使用する場合:
- PUT/POST リクエストの場合、Bun は自動的にマルチパートアップロードを使用します
- ストリームはチャンクで消費され、並列でアップロードされます
- 進行状況は S3 オプションを通じて監視できます
タイムアウト付きで URL をフェッチ
タイムアウト付きで URL をフェッチするには、AbortSignal.timeout を使用します。
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});リクエストのキャンセル
リクエストをキャンセルするには、AbortController を使用します。
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();Unix ドメインソケット
Unix ドメインソケットを使用して URL をフェッチするには、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
クライアント証明書を使用するには、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")],
},
});カスタム TLS 検証
TLS 検証をカスタマイズするには、tls の checkServerIdentity オプションを使用します。
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// 証明書が無効な場合に Error を返す
},
},
});これは Node の net モジュールでの動作と似ています。
TLS 検証の無効化
TLS 検証を無効にするには、rejectUnauthorized を false に設定します。
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});これは自己署名証明書を使用する際に SSL エラーを回避するために特に役立ちますが、TLS 検証を無効にするため、注意して使用する必要があります。
リクエストオプション
標準の fetch オプションに加えて、Bun はいくつかの拡張を提供します。
const response = await fetch("http://example.com", {
// 自動レスポンス解凍を制御(デフォルト:true)
// gzip、deflate、brotli(br)、zstd をサポート
decompress: true,
// このリクエストの接続再利用を無効化
keepalive: false,
// デバッグログレベル
verbose: true, // またはより詳細な出力の場合は "curl"
});プロトコルサポート
HTTP(S) を超えて、Bun の fetch はいくつかの追加プロトコルをサポートしています。
S3 URLs - s3://
Bun は S3 バケットから直接フェッチすることをサポートしています。
// 認証情報に環境変数を使用
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",
},
});注:S3 を使用する場合、PUT および POST メソッドのみがリクエストボディをサポートします。アップロードの場合、Bun はストリーミングボディのために自動的にマルチパートアップロードを使用します。
Bun の S3 サポートの詳細については、S3 ドキュメントを参照してください。
File URLs - file://
file: プロトコルを使用してローカルファイルをフェッチできます。
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();Windows では、パスは自動的に正規化されます。
// 両方とも 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 は data: URL スキームをサポートしています。
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"Blob URLs - blob:
URL.createObjectURL() で作成された URL を使用して Blob をフェッチできます。
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);エラーハンドリング
Bun の fetch 実装にはいくつかの特定のエラーケースが含まれています。
- GET/HEAD メソッドでリクエストボディを使用するとエラーがスローされます(fetch API では予想されます)
proxyオプションとunixオプションの両方を一緒に使用しようとするとエラーがスローされますrejectUnauthorizedが true(または未定義)の場合の TLS 証明書検証の失敗- S3 操作は認証または権限に関連する特定のエラーをスローする可能性があります
Content-Type ヘッダーの処理
Bun は、明示的に提供されていない場合にリクエストボディの Content-Type ヘッダーを自動的に設定します。
Blobオブジェクトの場合、blob のtypeを使用FormDataの場合、適切なマルチパート境界を設定
デバッグ
デバッグを支援するために、verbose: true を fetch に渡すことができます。
const response = await fetch("http://example.com", {
verbose: true,
});これにより、リクエストとレスポンスのヘッダーがターミナルに出力されます。
[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 は Web 標準の fetch API の一部ではなく、Bun 固有のものです。
パフォーマンス
HTTP リクエストを送信する前に、DNS ルックアップを実行する必要があります。これは、DNS サーバーが遅い場合やネットワーク接続が悪い場合に、特に時間がかかる可能性があります。
DNS ルックアップの後、TCP ソケットを接続する必要があり、TLS ハンドシェイクを実行する必要があるかもしれません。これもまた、かなりの時間がかかる可能性があります。
リクエストが完了した後、レスポンスボディの消費にもかなりの時間とメモリがかかる可能性があります。
Bun は、アプリケーションのパフォーマンスを最適化するための API を各ステップで提供します。
DNS プリフェッチ
DNS エントリをプリフェッチするには、dns.prefetch API を使用できます。この API は、近い将来ホストに接続する必要があり、初期 DNS ルックアップを回避したい場合に役立ちます。
import { dns } from "bun";
dns.prefetch("bun.com");DNS キャッシング
デフォルトで、Bun は DNS クエリをメモリ内で最大 30 秒間キャッシュし、重複排除します。dns.getCacheStats() を呼び出すことでキャッシュ統計を確認できます。
Bun での DNS キャッシングの詳細については、DNS キャッシング ドキュメントを参照してください。
ホストへのプリコネクト
ホストにプリコネクトするには、fetch.preconnect API を使用できます。この API は、近い将来ホストに接続する必要があり、初期 DNS ルックアップ、TCP ソケット接続、TLS ハンドシェイクを早期に開始したい場合に役立ちます。
import { fetch } from "bun";
fetch.preconnect("https://bun.com");注:fetch.preconnect の直後に fetch を呼び出しても、リクエストは高速になりません。プリコネクトは、ホストにすぐに接続する必要があるが、まだリクエストを行う準備ができていない場合にのみ役立ちます。
起動時のプリコネクト
起動時にホストにプリコネクトするには、--fetch-preconnect を渡します。
bun --fetch-preconnect https://bun.com ./my-script.tsこれは HTML の <link rel="preconnect"> のようなものです。
この機能はまだ Windows では実装されていません。Windows でこの機能を使用することに興味がある場合は、issue を作成してください。Windows でのサポートを実装します。
接続プーリングと HTTP keep-alive
Bun は同じホストへの接続を自動的に再利用します。これは接続プーリングとして知られています。これにより、接続の確立にかかる時間を大幅に短縮できます。これを有効にするために何もする必要はありません。自動的に有効になります。
同時接続数の制限
デフォルトで、Bun は同時 fetch リクエストの最大数を 256 に制限しています。これにはいくつかの理由があります。
- システム全体の安定性が向上します。オペレーティングシステムには同時オープン TCP ソケットの数に上限があり、通常は数千程度です。この制限に近づくと、コンピューター全体の動作が不安定になります。アプリケーションがハングしたりクラッシュしたりします。
- HTTP Keep-Alive 接続の再利用を促進します。短命の HTTP リクエストの場合、最も遅いステップは多くの場合、初期接続のセットアップです。接続を再利用することで、多くの時間を節約できます。
制限を超えると、リクエストはキューに入れられ、次のリクエストが終了するとすぐに送信されます。
BUN_CONFIG_MAX_HTTP_REQUESTS 環境変数を通じて、同時接続の最大数を増やすことができます。
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.tsこの制限の最大値は現在 65,336 に設定されています。最大ポート番号は 65,535 なので、1 台のコンピューターがこの制限を超えることは非常に困難です。
レスポンスバッファリング
Bun はレスポンスボディの読み取りパフォーマンスを最適化するために多大な努力を払っています。レスポンスボディを読み取る最速の方法は、これらのメソッドの 1 つを使用することです。
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 を使用して、レスポンスボディをディスク上のファイルに書き込むこともできます。
import { write } from "bun";
await write("output.txt", response);実装の詳細
- 接続プーリングはデフォルトで有効ですが、
keepalive: falseでリクエストごとに無効にできます。"Connection: close"ヘッダーを使用して keep-alive を無効にすることもできます。 - 大きなファイルのアップロードは、特定の条件下でオペレーティングシステムの
sendfileシステムコールを使用して最適化されます。- ファイルは 32KB よりも大きい必要があります
- リクエストはプロキシを使用していない必要があります
- macOS では、通常ファイル(パイプ、ソケット、デバイスではない)のみが
sendfileを使用できます - これらの条件が満たされない場合、または S3/ストリーミングアップロードを使用する場合、Bun はファイルをメモリに読み込むフォールバックを行います
- この最適化は、ファイルがカーネルからネットワークスタックに直接送信できる HTTP(HTTPS ではない)リクエストで特に効果的です
- S3 操作は自動的にリクエストの署名と認証ヘッダーのマージを処理します
注:これらの機能の多くは、標準の fetch API に対する Bun 固有の拡張です。