Skip to content

基本設定

ts
const server = Bun.serve({
  // `routes` には Bun v1.2.3+ が必要です
  routes: {
    // 静的ルート
    "/api/status": new Response("OK"),

    // 動的ルート
    "/users/:id": req => {
      return new Response(`Hello User ${req.params.id}!`);
    },

    // HTTP メソッドごとのハンドラー
    "/api/posts": {
      GET: () => new Response("List posts"),
      POST: async req => {
        const body = await req.json();
        return Response.json({ created: true, ...body });
      },
    },

    // "/api/" で始まり、他に一致しないすべてのルートのワイルドカードルート
    "/api/*": Response.json({ message: "Not found" }, { status: 404 }),

    // /blog/hello から /blog/hello/world へのリダイレクト
    "/blog/hello": Response.redirect("/blog/hello/world"),

    // ファイルをメモリに遅延読み込みして提供
    "/favicon.ico": Bun.file("./favicon.ico"),
  },

  // (オプション)一致しなかったルートのフォールバック:
  // Bun のバージョンが 1.2.3 未満の場合は必須
  fetch(req) {
    return new Response("Not Found", { status: 404 });
  },
});

console.log(`サーバー実行中:${server.url}`);

HTML インポート

Bun はサーバーコードに HTML ファイルを直接インポートする機能をサポートしており、サーバーサイドとクライアントサイドの両方のコードを備えたフルスタックアプリケーションを可能にします。HTML インポートは 2 つのモードで動作します。

開発(bun --hot): アセットはランタイムでオンデマンドにバンドルされ、ホットモジュールリプレースメント(HMR)を有効にして、反復的な開発体験を高速化します。フロントエンドコードを変更すると、ページ全体をリロードせずにブラウザが自動的に更新されます。

プロダクション(bun build): bun build --target=bun でビルドすると、import index from "./index.html" 文は、バンドルされたすべてのクライアントアセットを含む事前ビルドされたマニフェストオブジェクトに解決されます。Bun.serve はこのマニフェストを使用して、ランタイムバンドルのオーバーヘッドゼロで最適化されたアセットを提供します。これはプロダクションへのデプロイに最適です。

ts
import myReactSinglePageApp from "./index.html";

Bun.serve({
  routes: {
    "/": myReactSinglePageApp,
  },
});

HTML インポートは HTML を提供するだけではありません。Bun の バンドラー、JavaScript トランスパイラー、CSS パーサーを使用して構築された、フル機能のフロントエンドバンドラー、トランスパイラー、ツールキットです。これを使用して、React、TypeScript、Tailwind CSS などを備えたフル機能のフロントエンドを構築できます。

HTML インポートを使用したフルスタックアプリケーションの構築に関する完全なガイドについては、/docs/bundler/fullstack を参照してください。


設定

porthostname の変更

サーバーがリッスンするポートとホスト名を設定するには、オプションオブジェクトで porthostname を設定します。

ts
Bun.serve({
  port: 8080, // デフォルトは $BUN_PORT、$PORT、$NODE_PORT、それ以外は 3000
  hostname: "mydomain.com", // デフォルトは "0.0.0.0"
  fetch(req) {
    return new Response("404!");
  },
});

使用可能なポートをランダムに選択するには、port0 に設定します。

ts
const server = Bun.serve({
  port: 0, // ランダムなポート
  fetch(req) {
    return new Response("404!");
  },
});

// server.port はランダムに選択されたポートです
console.log(server.port);

サーバーオブジェクトの port プロパティにアクセスするか、url プロパティにアクセスして、選択されたポートを表示できます。

ts
console.log(server.port); // 3000
console.log(server.url); // http://localhost:3000

デフォルトポートの設定

Bun はデフォルトポートを設定するためのいくつかのオプションと環境変数をサポートしています。デフォルトポートは port オプションが設定されていない場合に使用されます。

  • --port CLI フラグ
sh
bun --port=4002 server.ts
  • BUN_PORT 環境変数
sh
BUN_PORT=4002 bun server.ts
  • PORT 環境変数
sh
PORT=4002 bun server.ts
  • NODE_PORT 環境変数
sh
NODE_PORT=4002 bun server.ts

Unix ドメインソケット

Unix ドメインソケット でリッスンするには、ソケットへのパスを指定して unix オプションを渡します。

ts
Bun.serve({
  unix: "/tmp/my-socket.sock", // ソケットへのパス
  fetch(req) {
    return new Response(`404!`);
  },
});

アブストラクトネームスペースソケット

Bun は Linux アブストラクトネームスペースソケットをサポートしています。アブストラクトネームスペースソケットを使用するには、unix パスの前にヌルバイトを付けます。

ts
Bun.serve({
  unix: "\0my-abstract-socket", // アブストラクトネームスペースソケット
  fetch(req) {
    return new Response(`404!`);
  },
});

Unix ドメインソケットとは異なり、アブストラクトネームスペースソケットはファイルシステムにバインドされず、ソケットへの最後の参照が閉じられると自動的に削除されます。


idleTimeout

アイドルタイムアウトを設定するには、Bun.serve の idleTimeout フィールドを設定します。

ts
Bun.serve({
  // 10 秒:
  idleTimeout: 10,

  fetch(req) {
    return new Response("Bun!");
  },
});

これは、サーバーが接続を閉じる前に接続がアイドルであることが許容される最大時間です。データが送信または受信されない場合、接続はアイドル状態と見なされます。


export default 構文

これまでのこのページの例では、明示的な Bun.serve API を使用していました。Bun は別の構文もサポートしています。

ts
import { type Serve } from "bun";

export default {
  fetch(req) {
    return new Response("Bun!");
  },
} satisfies Serve;

サーバーオプションを Bun.serve に渡す代わりに、export default します。このファイルはそのまま実行できます。Bun が fetch ハンドラーを含む default エクスポートを含むファイルを検出すると、内部的に Bun.serve に渡します。


ホットルートリローディング

server.reload() を使用して、サーバーを再起動せずにルートを更新できます。

ts
const server = Bun.serve({
  routes: {
    "/api/version": () => Response.json({ version: "1.0.0" }),
  },
});

// ダウンタイムなしで新しいルートをデプロイ
server.reload({
  routes: {
    "/api/version": () => Response.json({ version: "2.0.0" }),
  },
});

サーバーライフサイクルメソッド

server.stop()

サーバーが新しい接続を受け入れるのを停止するには:

ts
const server = Bun.serve({
  fetch(req) {
    return new Response("Hello!");
  },
});

// 正常にサーバーを停止(進行中のリクエストを待機)
await server.stop();

// 強制的に停止し、すべてのアクティブな接続を閉じる
await server.stop(true);

デフォルトでは、stop() は進行中のリクエストと WebSocket 接続が完了するのを許可します。すべての接続を即座に終了するには true を渡します。

server.ref()server.unref()

サーバーが Bun プロセスを存続させるかどうかを制御します。

ts
// サーバーが唯一実行中の場合、プロセスを存続させない
server.unref();

// デフォルトの動作を復元 - プロセスを存続させる
server.ref();

server.reload()

サーバーのハンドラーを再起動せずに更新します。

ts
const server = Bun.serve({
  routes: {
    "/api/version": Response.json({ version: "v1" }),
  },
  fetch(req) {
    return new Response("v1");
  },
});

// 新しいハンドラーに更新
server.reload({
  routes: {
    "/api/version": Response.json({ version: "v2" }),
  },
  fetch(req) {
    return new Response("v2");
  },
});

これは開発とホットリロードに役立ちます。更新できるのは fetcherrorroutes のみです。


リクエストごとの制御

server.timeout(Request, seconds)

個々のリクエストのカスタムアイドルタイムアウトを設定します。

ts
const server = Bun.serve({
  async fetch(req, server) {
    // このリクエストの 60 秒のタイムアウトを設定
    server.timeout(req, 60);

    // 60 秒を超えてボディを送信しない場合、リクエストは中止されます
    await req.text();

    return new Response("Done!");
  },
});

リクエストのタイムアウトを無効にするには 0 を渡します。

server.requestIP(Request)

クライアント IP とポート情報を取得します。

ts
const server = Bun.serve({
  fetch(req, server) {
    const address = server.requestIP(req);
    if (address) {
      return new Response(`クライアント IP: ${address.address}, ポート:${address.port}`);
    }
    return new Response("不明なクライアント");
  },
});

閉じられたリクエストまたは Unix ドメインソケットの場合は null を返します。


サーバーメトリクス

server.pendingRequestsserver.pendingWebSockets

組み込みカウンターを使用してサーバーアクティビティを監視します。

ts
const server = Bun.serve({
  fetch(req, server) {
    return new Response(
      `アクティブなリクエスト:${server.pendingRequests}\n` + `アクティブな WebSocket: ${server.pendingWebSockets}`,
    );
  },
});

server.subscriberCount(topic)

WebSocket トピックの購読者数を取得します。

ts
const server = Bun.serve({
  fetch(req, server) {
    const chatUsers = server.subscriberCount("chat");
    return new Response(`${chatUsers} 人がチャット中`);
  },
  websocket: {
    message(ws) {
      ws.subscribe("chat");
    },
  },
});

ベンチマーク

以下は、受信するすべての RequestBun! と応答するシンプルな HTTP サーバーの Bun と Node.js の実装です。

ts
Bun.serve({
  fetch(req: Request) {
    return new Response("Bun!");
  },
  port: 3000,
});
ts
require("http")
  .createServer((req, res) => res.end("Bun!"))
  .listen(8080);

Bun.serve サーバーは、Linux 上で Node.js よりも約 2.5 倍多くのリクエストを 1 秒あたりに処理できます。

ランタイム1 秒あたりのリクエスト数
Node 16~64,000
Bun~160,000

実用的な例:REST API

これは依存関係ゼロで Bun のルーターを使用した基本的なデータベースバックエンド REST API です。

ts
import type { Post } from "./types.ts";
import { Database } from "bun:sqlite";

const db = new Database("posts.db");
db.exec(`
  CREATE TABLE IF NOT EXISTS posts (
    id TEXT PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    created_at TEXT NOT NULL
  )
`);

Bun.serve({
  routes: {
    // ポストの一覧
    "/api/posts": {
      GET: () => {
        const posts = db.query("SELECT * FROM posts").all();
        return Response.json(posts);
      },

      // ポストの作成
      POST: async req => {
        const post: Omit<Post, "id" | "created_at"> = await req.json();
        const id = crypto.randomUUID();

        db.query(
          `INSERT INTO posts (id, title, content, created_at)
           VALUES (?, ?, ?, ?)`,
        ).run(id, post.title, post.content, new Date().toISOString());

        return Response.json({ id, ...post }, { status: 201 });
      },
    },

    // ID でポストを取得
    "/api/posts/:id": req => {
      const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id);

      if (!post) {
        return new Response("Not Found", { status: 404 });
      }

      return Response.json(post);
    },
  },

  error(error) {
    console.error(error);
    return new Response("Internal Server Error", { status: 500 });
  },
});
ts
export interface Post {
  id: string;
  title: string;
  content: string;
  created_at: string;
}

リファレンス

ts
interface Server extends Disposable {
  /**
   * 新しい接続を受け入れるのをサーバーを停止します。
   * @param closeActiveConnections true の場合、すべての接続を即座に終了します
   * @returns サーバーが停止したときに解決される Promise
   */
  stop(closeActiveConnections?: boolean): Promise<void>;

  /**
   * サーバーを再起動せずにハンドラーを更新します。
   * fetch と error ハンドラーのみ更新できます。
   */
  reload(options: Serve): void;

  /**
   * 実行中のサーバーにリクエストを作成します。
   * テストや内部ルーティングに役立ちます。
   */
  fetch(request: Request | string): Response | Promise<Response>;

  /**
   * HTTP リクエストを WebSocket 接続にアップグレードします。
   * @returns アップグレード成功時は true、失敗時は false
   */
  upgrade<T = undefined>(
    request: Request,
    options?: {
      headers?: Bun.HeadersInit;
      data?: T;
    },
  ): boolean;

  /**
   * トピックを購読しているすべての WebSocket クライアントにメッセージを公開します。
   * @returns 送信されたバイト数、ドロップされた場合は 0、バックプレッシャーが適用された場合は -1
   */
  publish(
    topic: string,
    data: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer,
    compress?: boolean,
  ): ServerWebSocketSendStatus;

  /**
   * トピックを購読している WebSocket クライアントの数を取得します。
   */
  subscriberCount(topic: string): number;

  /**
   * クライアントの IP アドレスとポートを取得します。
   * @returns 閉じられたリクエストまたは Unix ソケットの場合は null
   */
  requestIP(request: Request): SocketAddress | null;

  /**
   * リクエストのカスタムアイドルタイムアウトを設定します。
   * @param seconds タイムアウト(秒)、0 で無効
   */
  timeout(request: Request, seconds: number): void;

  /**
   * サーバー実行中にプロセスを存続させ続けます。
   */
  ref(): void;

  /**
   * サーバーが唯一実行中の場合、プロセスを終了できるようにします。
   */
  unref(): void;

  /** 進行中の HTTP リクエストの数 */
  readonly pendingRequests: number;

  /** アクティブな WebSocket 接続の数 */
  readonly pendingWebSockets: number;

  // プロトコル、ホスト名、ポートを含むサーバー URL
  readonly url: URL;

  /** サーバーがリッスンしているポート */
  readonly port: number;

  /** サーバーがバインドされているホスト名 */
  readonly hostname: string;

  /** サーバーが開発モードかどうか */
  readonly development: boolean;

  /** サーバーインスタンスの識別子 */
  readonly id: string;
}

interface WebSocketHandler<T = undefined> {
  /** 最大 WebSocket メッセージサイズ(バイト) */
  maxPayloadLength?: number;

  /** バックプレッシャーを適用する前にキューイングされたメッセージのバイト数 */
  backpressureLimit?: number;

  /** バックプレッシャー制限に達したときに接続を閉じるかどうか */
  closeOnBackpressureLimit?: boolean;

  /** バックプレッシャーが緩和されたときに呼び出されます */
  drain?(ws: ServerWebSocket<T>): void | Promise<void>;

  /** アイドルタイムアウトまでの秒数 */
  idleTimeout?: number;

  /** メッセージごとの deflate 圧縮を有効にする */
  perMessageDeflate?:
    | boolean
    | {
        compress?: WebSocketCompressor | boolean;
        decompress?: WebSocketCompressor | boolean;
      };

  /** 接続を維持するために ping フレームを送信する */
  sendPings?: boolean;

  /** サーバーが自分で公開したメッセージを受信するかどうか */
  publishToSelf?: boolean;

  /** 接続がオープンされたときに呼び出されます */
  open?(ws: ServerWebSocket<T>): void | Promise<void>;

  /** メッセージを受信したときに呼び出されます */
  message(ws: ServerWebSocket<T>, message: string | Buffer): void | Promise<void>;

  /** 接続が閉じられたときに呼び出されます */
  close?(ws: ServerWebSocket<T>, code: number, reason: string): void | Promise<void>;

  /** ping フレームを受信したときに呼び出されます */
  ping?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;

  /** pong フレームを受信したときに呼び出されます */
  pong?(ws: ServerWebSocket<T>, data: Buffer): void | Promise<void>;
}

interface TLSOptions {
  /** 認証局チェーン */
  ca?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;

  /** サーバー証明書 */
  cert?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;

  /** DH パラメーターファイルへのパス */
  dhParamsFile?: string;

  /** 秘密鍵 */
  key?: string | Buffer | BunFile | Array<string | Buffer | BunFile>;

  /** TLS メモリ使用量を削減 */
  lowMemoryMode?: boolean;

  /** 秘密鍵のパスフレーズ */
  passphrase?: string;

  /** OpenSSL オプションフラグ */
  secureOptions?: number;

  /** SNI のサーバー名 */
  serverName?: string;
}

Bun by www.bunjs.com.cn 編集