Bun

Bun 完全概要 ─ 次世代 JavaScript/TypeScript オールインワンツールキット

1. はじめに

1.1 Bun とは何か

Bun は、JavaScript および TypeScript アプリケーション開発のためのオールインワンツールキットである。単一の実行可能バイナリ bun として配布され、以下の4つの主要機能を統合している。

機能説明対応する既存ツール
ランタイムJavaScript/TypeScript ファイルの実行Node.js, Deno
パッケージマネージャー依存関係のインストール・管理npm, yarn, pnpm
テストランナーJest 互換のテスト実行Jest, Vitest
バンドラーJS/TS/JSX/CSS のバンドルesbuild, webpack, Rollup

1.2 設計思想と技術的基盤

Bun は以下の設計目標に基づいて開発されている。

高速性: Bun は Zig 言語で記述されており、Apple が Safari 向けに開発した JavaScriptCore (JSC) エンジンを内部で使用する。Node.js が使用する V8 エンジンと比較して、起動時間が約4倍高速であり、メモリ使用量も大幅に削減される。

# ベンチマーク比較(Hello World スクリプト、Linux)
bun hello.js   → 5.2ms
node hello.js  → 25.1ms

TypeScript/JSX ネイティブサポート: .ts, .tsx, .jsx ファイルを事前設定なしで直接実行可能。Bun の内蔵トランスパイラが実行前にバニラ JavaScript へ変換する。

ESM と CommonJS の互換性: ES Modules (ESM) を推奨しつつ、CommonJS もサポート。既存の npm パッケージとの互換性を維持する。

Web 標準 API: fetch, WebSocket, ReadableStream, Request, Response, Headers, URL など、Web 標準 API をネイティブに実装。

Node.js 互換性: process, Buffer, __dirname などの Node.js グローバル変数や、node:fs, node:path, node:http などの組み込みモジュールとの互換性を目指す。

1.3 インストール

# macOS / Linux
curl -fsSL https://bun.sh/install | bash

# Homebrew
brew install oven-sh/bun/bun

# npm 経由
npm install -g bun

# Windows (PowerShell)
powershell -c "irm bun.sh/install.ps1 | iex"

# Docker
docker pull oven/bun

インストール後のバージョン確認:

bun --version
# 1.x.x

1.4 クイックスタート

# プロジェクト初期化
mkdir my-app && cd my-app
bun init

# TypeScript ファイルの直接実行
echo 'console.log("Hello, Bun!")' > index.ts
bun run index.ts

# HTTP サーバーの起動
cat > server.ts << 'EOF'
Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello from Bun!");
  },
});
console.log("Server running at http://localhost:3000");
EOF
bun run server.ts

2. ランタイム

2.1 ファイルの実行

Bun ランタイムの最も基本的な機能は、JavaScript/TypeScript ファイルの実行である。

# 基本的な実行
bun run index.js
bun run index.ts
bun run index.tsx
bun run index.jsx

# "run" キーワードは省略可能
bun index.ts

2.2 ウォッチモードとホットリロード

ウォッチモード (--watch): ファイル変更を検知してプロセスを自動再起動する。

bun --watch run index.tsx

ホットリロード (--hot): プロセスを再起動せずにモジュールをインプレースで更新する。Bun.serve と組み合わせるとフロントエンド開発でHMR(Hot Module Replacement)が利用可能。

bun --hot run server.ts

2.3 package.json スクリプトの実行

// package.json
{
  "scripts": {
    "dev": "bun run server.ts",
    "build": "bun build ./src/index.ts --outdir ./dist",
    "clean": "rm -rf dist && echo 'Done.'",
    "test": "bun test"
  }
}
# スクリプト実行(npm run の約28倍高速)
bun run dev
bun run build

# 利用可能なスクリプト一覧
bun run

# --bun フラグ: Node.js の代わりに Bun で CLI を実行
bun run --bun vite

# フィルタリング(モノレポでの利用)
bun run --filter 'pkg-*' build
bun run --filter '!pkg-c' test

# 並列・逐次実行
bun run --parallel build lint
bun run --sequential test:unit test:e2e

2.4 環境変数と .env ファイル

Bun は .env ファイルを自動的に読み込む。

# .env
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret123

# .env.local(ローカル上書き)
API_KEY=local_secret
// process.env 経由でアクセス
console.log(process.env.DATABASE_URL);

// Bun.env 経由でもアクセス可能(型安全)
console.log(Bun.env.API_KEY);
# カスタム .env ファイルの指定
bun --env-file=.env.production run server.ts

# .env の自動読み込みを無効化(bunfig.toml)
# env = false

2.5 モジュール解決

Bun は Node.js 互換のモジュール解決アルゴリズムを実装しつつ、拡張も行っている。

対応モジュール形式:

  • ESM (import/export)
  • CommonJS (require/module.exports)
  • 両形式の混在も可能

解決順序:

  1. package.json"exports" フィールド
  2. package.json"main" フィールド
  3. index.{ts,tsx,js,jsx,json}

パス解決:

// 相対パス
import { foo } from "./utils";

// パッケージ
import React from "react";

// Bun 組み込みモジュール
import { serve } from "bun";
import { Database } from "bun:sqlite";
import { test, expect } from "bun:test";

// Node.js 互換モジュール
import { readFile } from "node:fs/promises";
import path from "node:path";

自動インストール: node_modules が存在しない場合、Bun は実行時に依存関係を自動インストールする。

# bunfig.toml で自動インストールを制御
[install]
auto = "auto"      # デフォルト: node_modules がなければ自動インストール
# auto = "fallback"  # 見つからないパッケージのみ自動インストール
# auto = "force"     # 常に自動インストール
# auto = "disable"   # 無効化

2.6 TypeScript の設定

Bun は tsconfig.json を自動的に読み込む。Bun 固有の設定は bunfig.toml でも可能。

// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "types": ["bun-types"]
  }
}
# Bun 型定義のインストール
bun add -d @types/bun

2.7 デバッグ

Bun は WebKit Inspector Protocol に基づくデバッガーを内蔵している。

# デバッガーを有効化
bun --inspect run index.ts

# 接続を待ってから実行
bun --inspect-wait run index.ts

# 最初の行にブレークポイントを設定して待機
bun --inspect-brk run index.ts

デバッガーは VS Code や Chrome DevTools から接続可能。

2.8 プリロード

特定のスクリプトをメインファイルの前に実行する。

# CLI フラグ
bun --preload ./setup.ts run index.ts

# bunfig.toml
# preload = ["./setup.ts"]
// setup.ts - プラグインの登録など
import { plugin } from "bun";

plugin({
  name: "YAML loader",
  setup(build) {
    build.onLoad({ filter: /\.yaml$/ }, async (args) => {
      const text = await Bun.file(args.path).text();
      // YAML をパースして JS オブジェクトとして返す
      return {
        contents: `export default ${JSON.stringify(/* parse yaml */)}`,
        loader: "js",
      };
    });
  },
});

3. パッケージマネージャー

Bun のパッケージマネージャーは、npm, yarn, pnpm のドロップイン代替として設計されており、最大25倍の高速化を実現する。グローバルキャッシュと最適化されたシステムコールにより、既存プロジェクトへの導入も容易である。

3.1 基本操作

# 全依存関係のインストール
bun install

# パッケージの追加
bun add react
bun add react@19.1.1          # バージョン指定
bun add react@latest           # 最新タグ
bun add -d typescript          # devDependencies
bun add --optional sharp       # optionalDependencies
bun add --peer react           # peerDependencies
bun add --exact lodash         # 正確なバージョン(^ なし)

# パッケージの削除
bun remove react

# パッケージの更新
bun update react

# グローバルインストール
bun install -g cowsay

# パッケージの実行(npx 相当)
bunx cowsay "Hello!"

3.2 ロックファイル

Bun 1.2 以降、テキスト形式の bun.lock がデフォルトのロックファイルとなった(以前はバイナリ形式の bun.lockb)。

# 旧形式からの移行
bun install --save-text-lockfile --frozen-lockfile --lockfile-only
rm bun.lockb

# ロックファイルに厳密に従うインストール(CI 向け)
bun install --frozen-lockfile
# または
bun ci

3.3 ワークスペース(モノレポ)

// package.json(ルート)
{
  "name": "my-monorepo",
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}
# 特定のワークスペースのみインストール
bun install --filter './packages/pkg-a'

# 特定ワークスペース以外をインストール
bun install --filter '!pkg-c'

# ワークスペース全体でスクリプト実行
bun run --filter '*' build

3.4 オーバーライドと解決

メタ依存関係(依存関係の依存関係)のバージョンを強制指定できる。

{
  "dependencies": {
    "foo": "^2.0.0"
  },
  "overrides": {
    "bar": "~4.4.0"
  },
  // Yarn 互換
  "resolutions": {
    "baz": "1.0.0"
  }
}

3.5 パッチ機能

# パッチの作成
bun patch <package-name>
# 変更を加えた後
bun patch --commit <package-name>

3.6 インストール戦略

Bun は2つのインストール戦略をサポートする。

Hoisted(ホイスト): npm/Yarn の伝統的なフラット化アプローチ。

bun install --linker hoisted

Isolated(分離): pnpm に類似した厳密な依存関係分離。ファントム依存関係を防止する。

bun install --linker isolated
# デフォルトの選択基準:
# 新規ワークスペース/モノレポ → isolated
# 新規単一パッケージ → hoisted
# 既存プロジェクト(v1.3.2以前) → hoisted

3.7 セキュリティ機能

ライフサイクルスクリプトの制限: Bun はインストールされた依存関係の任意のライフサイクルスクリプト(postinstall 等)をデフォルトで実行しない。

{
  "trustedDependencies": ["my-trusted-package"]
}

最小リリース期間: 新しく公開されたパッケージのインストールを制限する。

# bunfig.toml
[install]
# 3日以上経過したパッケージのみインストール
minimumReleaseAge = 259200
minimumReleaseAgeExcludes = ["@types/node", "typescript"]

セキュリティスキャナー API: カスタムセキュリティスキャナーを構成可能。

# bunfig.toml
[install.security]
scanner = "@acme/bun-security-scanner"

3.8 非 npm 依存関係

{
  "dependencies": {
    "dayjs": "git+https://github.com/iamkun/dayjs.git",
    "lodash": "git+ssh://github.com/lodash/lodash.git#4.17.21",
    "zod": "github:colinhacks/zod",
    "react": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
    "bun-types": "npm:@types/bun"
  }
}

3.9 pnpm からの移行

pnpm-lock.yaml が検出され bun.lock が存在しない場合、Bun は自動的にロックファイルを移行する。

# 自動移行
bun install
# pnpm-lock.yaml → bun.lock に変換
# pnpm-workspace.yaml → package.json の workspaces に移行
# catalog: プロトコルも保持

3.10 CI/CD 設定

# .github/workflows/ci.yml
name: CI
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun ci              # frozen-lockfile 相当
      - run: bun run build
      - run: bun test

3.11 プラットフォーム固有の設定

# クロスプラットフォームビルド用
bun install --cpu=x64 --os=linux

# 対応 CPU: arm64, x64, ia32, ppc64, s390x
# 対応 OS: linux, darwin, win32, freebsd, openbsd, sunos, aix

3.12 キャッシュ管理

# キャッシュディレクトリ: ~/.bun/install/cache
bun pm cache rm

# 強制再インストール
bun install --force

4. バンドラー

Bun のネイティブバンドラーは、esbuild の three.js ベンチマークで esbuild より1.75倍高速な結果を示す。JavaScript API (Bun.build()) と CLI (bun build) の両方から利用可能。

4.1 基本的な使い方

// JavaScript API
const result = await Bun.build({
  entrypoints: ["./src/index.tsx"],
  outdir: "./dist",
});

if (!result.success) {
  for (const log of result.logs) {
    console.error(log);
  }
}
# CLI
bun build ./src/index.tsx --outdir ./dist

# ウォッチモード
bun build ./src/index.tsx --outdir ./dist --watch

4.2 ターゲット

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  target: "browser",   // ブラウザ向け(デフォルト)
  // target: "bun",     // Bun ランタイム向け
  // target: "node",    // Node.js 向け
});
bun build ./src/index.ts --outdir ./dist --target browser
bun build ./src/index.ts --outdir ./dist --target bun
bun build ./src/index.ts --outdir ./dist --target node

4.3 出力フォーマット

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  format: "esm",      // ES Modules(デフォルト)
  // format: "cjs",    // CommonJS(実験的)
  // format: "iife",   // IIFE(実験的)
});

4.4 コード分割

await Bun.build({
  entrypoints: [
    "./src/index.ts",
    "./src/admin.ts",
  ],
  outdir: "./dist",
  splitting: true,     // 共通コードを自動分割
});

4.5 ソースマップ

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  sourcemap: "external",  // 外部ファイル(.map)
  // sourcemap: "inline",  // インライン
  // sourcemap: "linked",  // リンク付き外部ファイル
  // sourcemap: "none",    // 無効(デフォルト)
});

4.6 最小化

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  minify: true,            // 全ての最小化を有効化
  // または個別に指定:
  // minify: {
  //   whitespace: true,    // 空白の除去
  //   identifiers: true,   // 変数名の短縮
  //   syntax: true,        // 構文の最適化
  // },
});

4.7 プラグイン

import type { BunPlugin } from "bun";

const yamlPlugin: BunPlugin = {
  name: "YAML loader",
  setup(build) {
    build.onLoad({ filter: /\.yaml$/ }, async (args) => {
      const text = await Bun.file(args.path).text();
      const yaml = require("yaml");
      return {
        contents: `export default ${JSON.stringify(yaml.parse(text))}`,
        loader: "js",
      };
    });
  },
};

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  plugins: [yamlPlugin],
});

プラグインのライフサイクルフック:

フック説明
onStart()バンドル開始時に1回実行
onResolve()モジュール解決前に実行
onLoad()モジュール読み込み前に実行
onBeforeParse()ファイルパース前に実行(ネイティブプラグイン)

4.8 外部モジュール

バンドルから特定モジュールを除外する。

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  external: ["express", "pg"],
});

4.9 Define と Drop

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  define: {
    "process.env.NODE_ENV": JSON.stringify("production"),
  },
  drop: ["console", "debugger"],  // console.* と debugger を削除
});

4.10 ローダー

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  loader: {
    ".svg": "file",       // ファイルとして出力
    ".txt": "text",       // テキストとしてインポート
    ".data": "json",      // JSON としてパース
  },
});

対応ローダー一覧:

ローダー説明
jsJavaScript
jsxJSX を含む JavaScript
tsTypeScript
tsxJSX を含む TypeScript
jsonJSON
tomlTOML
textテキスト(文字列として export)
fileファイル(URLとして export)
wasmWebAssembly
napiNode-API ネイティブモジュール
cssCSS

4.11 HTML インポートとフルスタック開発

Bun は HTML ファイルを直接インポートしてフルスタックアプリケーションを構築できる。

import homepage from "./index.html";

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

開発モード (bun --hot): アセットはランタイムでオンデマンドバンドルされ、HMR が有効。

本番モード (bun build): bun build --target=bun でプリビルドされたマニフェストを使用し、ゼロランタイムバンドルオーバーヘッド。

4.12 ネイティブプラグイン

高速化のため、NAPI モジュールとして実装するネイティブプラグインをサポート。マルチスレッドで実行可能。

import myNativeAddon from "./my-native-addon";

await Bun.build({
  entrypoints: ["./app.tsx"],
  plugins: [
    {
      name: "my-plugin",
      setup(build) {
        build.onBeforeParse(
          { namespace: "file", filter: "**/*.tsx" },
          {
            napiModule: myNativeAddon,
            symbol: "replace_foo_with_bar",
          },
        );
      },
    },
  ],
});

5. テストランナー

Bun は Jest 互換の高速テストランナーを内蔵している。TypeScript をネイティブサポートし、React SSR テスト266件を Jest がバージョン番号を表示するよりも速く実行する。

5.1 基本的な使い方

// math.test.ts
import { test, expect, describe } from "bun:test";

describe("数学関数", () => {
  test("2 + 2 = 4", () => {
    expect(2 + 2).toBe(4);
  });

  test("配列の長さ", () => {
    expect([1, 2, 3]).toHaveLength(3);
  });
});
# 全テスト実行
bun test

# ファイルフィルタ
bun test math
bun test ./test/specific-file.test.ts

# テスト名フィルタ
bun test --test-name-pattern "addition"
bun test -t "数学"

5.2 テストファイルの検出パターン

  • *.test.{js|jsx|ts|tsx}
  • *_test.{js|jsx|ts|tsx}
  • *.spec.{js|jsx|ts|tsx}
  • *_spec.{js|jsx|ts|tsx}

5.3 ライフサイクルフック

import { beforeAll, beforeEach, afterEach, afterAll } from "bun:test";

beforeAll(() => {
  // 全テスト前に1回実行(DB接続など)
});

beforeEach(() => {
  // 各テスト前に実行
});

afterEach(() => {
  // 各テスト後に実行
});

afterAll(() => {
  // 全テスト後に1回実行(DB切断など)
});
# プリロードスクリプトでグローバルセットアップ
bun test --preload ./setup.ts

5.4 モック

import { test, expect, mock, jest, spyOn } from "bun:test";

// mock 関数の作成
const fn = mock(() => 42);
// または jest.fn() も利用可能
const fn2 = jest.fn(() => 42);

test("mock のテスト", () => {
  fn();
  expect(fn).toHaveBeenCalled();
  expect(fn).toHaveBeenCalledTimes(1);
  expect(fn).toHaveReturnedWith(42);
});

// モジュールモック
mock.module("./config", () => ({
  default: { apiUrl: "http://test.local" },
}));

// spyOn
const obj = { method: () => "original" };
const spy = spyOn(obj, "method").mockReturnValue("mocked");

test("spyOn のテスト", () => {
  expect(obj.method()).toBe("mocked");
  expect(spy).toHaveBeenCalled();
});

5.5 スナップショットテスト

import { test, expect } from "bun:test";

test("スナップショット", () => {
  const user = { name: "Taro", age: 30 };
  expect(user).toMatchSnapshot();
});

test("インラインスナップショット", () => {
  expect({ a: 1 }).toMatchInlineSnapshot();
});
# スナップショットの更新
bun test --update-snapshots

5.6 並行テスト実行

import { test } from "bun:test";

// 個別のテストを並行実行
test.concurrent("並行テスト 1", async () => {
  await fetch("/api/endpoint1");
});

test.concurrent("並行テスト 2", async () => {
  await fetch("/api/endpoint2");
});

// 強制的に逐次実行
test.serial("逐次テスト 1", () => {
  // 共有状態に依存するテスト
});
# 全テストを並行実行
bun test --concurrent

# 最大同時実行数の制限(デフォルト: 20)
bun test --concurrent --max-concurrency 4

5.7 テストの再試行とリラン

# 失敗テストの再試行
bun test --retry 3

# 各テストを複数回実行(フレーク検出)
bun test --rerun-each 100

# ランダム順序で実行
bun test --randomize

# シードを指定して再現可能なランダム順序
bun test --seed 12345
// テスト個別のリトライ設定
test("不安定なテスト", { retry: 5 }, () => {
  // ...
});

5.8 タイムアウトとベイルアウト

# テストごとのタイムアウト(デフォルト: 5000ms)
bun test --timeout 20000

# N回失敗後に中断
bun test --bail        # 1回で中断
bun test --bail=10     # 10回で中断

5.9 カバレッジ

# カバレッジ有効化
bun test --coverage

# レポーター設定
bun test --coverage --coverage-reporter text
bun test --coverage --coverage-reporter lcov --coverage-dir ./coverage
# bunfig.toml でのカバレッジ設定
[test]
coverage = true
coverageThreshold = 0.9   # 90% カバレッジ必須
# または個別閾値
# coverageThreshold = { line = 0.7, function = 0.8, statement = 0.9 }
coverageSkipTestFiles = true
coverageReporter = ["text", "lcov"]
coverageDir = "coverage"
coveragePathIgnorePatterns = ["**/*.spec.ts", "src/utils/**"]

5.10 DOM テスト

// bunfig.toml で happy-dom を設定
// [test]
// preload = ["./happydom.ts"]

// happydom.ts
import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register();

// component.test.tsx
import { test, expect } from "bun:test";
import { render, screen } from "@testing-library/react";

test("コンポーネントのレンダリング", () => {
  render(<button>Click me</button>);
  expect(screen.getByText("Click me")).toBeDefined();
});

5.11 CI/CD レポーター

# GitHub Actions 自動検出(設定不要)
bun test

# JUnit XML レポート
bun test --reporter=junit --reporter-outfile=./test-results.xml

# ドットレポーター
bun test --dots

5.12 bunfig.toml でのテスト設定

[test]
root = "./__tests__"
preload = ["./setup.ts"]
timeout = 10000
retry = 2
coverage = true
coverageThreshold = 0.8
randomize = false
pathIgnorePatterns = ["vendor/**", "fixtures/**"]

[test.reporter]
dots = false
junit = "test-results.xml"

6. HTTP サーバー(Bun.serve)

Bun.serve は高性能な HTTP サーバーを提供する。Node.js の http.createServer と比較して約2.5倍のリクエスト処理能力を持つ。

6.1 基本的なサーバー

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

console.log(`Server running at ${server.url}`);

6.2 ルーティング(Bun v1.2.3+)

const server = Bun.serve({
  routes: {
    // 静的レスポンス
    "/api/status": new Response("OK"),

    // 動的ルート(パスパラメータ)
    "/users/:id": (req) => {
      return new Response(`User ${req.params.id}`);
    },

    // HTTP メソッド別ハンドラー
    "/api/posts": {
      GET: () => Response.json({ posts: [] }),
      POST: async (req) => {
        const body = await req.json();
        return Response.json({ created: true, ...body }, { status: 201 });
      },
      DELETE: (req) => new Response(null, { status: 204 }),
    },

    // ワイルドカードルート
    "/api/*": Response.json({ message: "Not found" }, { status: 404 }),

    // リダイレクト
    "/old-page": Response.redirect("/new-page"),

    // 静的ファイル配信
    "/favicon.ico": Bun.file("./public/favicon.ico"),
  },

  // ルートに一致しないリクエストのフォールバック
  fetch(req) {
    return new Response("Not Found", { status: 404 });
  },
});

6.3 TLS/HTTPS

Bun.serve({
  port: 443,
  tls: {
    key: Bun.file("./key.pem"),
    cert: Bun.file("./cert.pem"),
    // ca: Bun.file("./ca.pem"),           // CA 証明書
    // passphrase: "my-passphrase",        // 秘密鍵のパスフレーズ
    // lowMemoryMode: true,                // TLS メモリ使用量削減
  },
  fetch(req) {
    return new Response("Secure!");
  },
});

6.4 WebSocket

const server = Bun.serve({
  fetch(req, server) {
    // WebSocket アップグレード
    if (server.upgrade(req, {
      data: { userId: "user123" },
      headers: { "Set-Cookie": "session=abc" },
    })) {
      return; // アップグレード成功
    }
    return new Response("Not a WebSocket request", { status: 400 });
  },

  websocket: {
    // 接続オープン
    open(ws) {
      console.log(`Connected: ${ws.data.userId}`);
      ws.subscribe("chat");  // トピックの購読
    },

    // メッセージ受信
    message(ws, message) {
      // 全購読者にブロードキャスト
      ws.publish("chat", `${ws.data.userId}: ${message}`);
    },

    // 接続クローズ
    close(ws, code, reason) {
      ws.unsubscribe("chat");
    },

    // WebSocket 設定
    maxPayloadLength: 16 * 1024 * 1024,  // 最大メッセージサイズ(16MB)
    backpressureLimit: 1024 * 1024,       // バックプレッシャー制限
    idleTimeout: 120,                      // アイドルタイムアウト(秒)
    perMessageDeflate: true,               // 圧縮有効化
    sendPings: true,                       // Ping フレーム送信
  },
});

// サーバーからのブロードキャスト
server.publish("chat", "System: Server message");

// トピックの購読者数取得
console.log(server.subscriberCount("chat"));

6.5 Server-Sent Events (SSE)

Bun.serve({
  routes: {
    "/events": (req, server) => {
      // このリクエストのアイドルタイムアウトを無効化
      server.timeout(req, 0);

      return new Response(
        async function* () {
          let count = 0;
          while (true) {
            yield `data: ${JSON.stringify({ count: count++ })}\n\n`;
            await Bun.sleep(1000);
          }
        },
        {
          headers: {
            "Content-Type": "text/event-stream",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
          },
        },
      );
    },
  },
});

6.6 サーバーライフサイクル

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

// ホットルートリロード(ダウンタイムなし)
server.reload({
  routes: {
    "/api/version": () => Response.json({ version: "2.0.0" }),
  },
  fetch(req) {
    return new Response("Updated!");
  },
});

// グレースフル停止(進行中のリクエストを待つ)
await server.stop();

// 強制停止(全接続を即座に切断)
await server.stop(true);

// プロセスの維持制御
server.unref();  // サーバーが唯一の場合でもプロセス終了を許可
server.ref();    // デフォルト動作に戻す

6.7 サーバーメトリクス

const server = Bun.serve({
  fetch(req, server) {
    return new Response(
      `Active requests: ${server.pendingRequests}\n` +
      `Active WebSockets: ${server.pendingWebSockets}`
    );
  },
});

6.8 リクエスト情報

Bun.serve({
  fetch(req, server) {
    // クライアント IP の取得
    const addr = server.requestIP(req);
    if (addr) {
      console.log(`Client: ${addr.address}:${addr.port}`);
    }

    // リクエスト情報
    const url = new URL(req.url);
    console.log(`${req.method} ${url.pathname}`);
    console.log(`Headers:`, Object.fromEntries(req.headers));

    return new Response("OK");
  },
});

6.9 Unix ドメインソケット

Bun.serve({
  unix: "/tmp/my-server.sock",
  fetch(req) {
    return new Response("Unix socket server!");
  },
});

// 抽象名前空間ソケット(Linux)
Bun.serve({
  unix: "\0my-abstract-socket",
  fetch(req) {
    return new Response("Abstract namespace!");
  },
});

6.10 アイドルタイムアウト

Bun.serve({
  idleTimeout: 30,  // 30秒(デフォルト: 10秒、最大: 255)

  async fetch(req, server) {
    // リクエスト個別のタイムアウト設定
    server.timeout(req, 60);  // この接続は60秒

    // ストリーミング応答でタイムアウトを無効化
    // server.timeout(req, 0);

    return new Response("OK");
  },
});

6.11 export default 構文

// server.ts
import type { Serve } from "bun";

export default {
  port: 3000,
  fetch(req) {
    return new Response("Bun!");
  },
} satisfies Serve.Options<undefined>;

// bun run server.ts で自動的に Bun.serve に渡される

6.12 実践例: REST API with SQLite

import { Database } from "bun:sqlite";

const db = new Database("posts.db");
db.run(`
  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 { title, content } = await req.json();
        const id = crypto.randomUUID();
        db.query(
          "INSERT INTO posts (id, title, content, created_at) VALUES (?, ?, ?, ?)"
        ).run(id, title, content, new Date().toISOString());
        return Response.json({ id, title, content }, { status: 201 });
      },
    },
    "/api/posts/:id": (req) => {
      const post = db.query("SELECT * FROM posts WHERE id = ?").get(req.params.id);
      return post
        ? Response.json(post)
        : new Response("Not Found", { status: 404 });
    },
  },
  error(error) {
    console.error(error);
    return new Response("Internal Server Error", { status: 500 });
  },
});

7. 組み込み API

7.1 ファイル I/O

ファイルの読み取り (Bun.file()):

const file = Bun.file("data.txt");

// ファイル情報
console.log(file.size);  // バイト数
console.log(file.type);  // MIME タイプ
console.log(await file.exists());  // 存在チェック

// 様々な形式で読み取り
const text = await file.text();           // 文字列
const json = await file.json();           // JSON オブジェクト
const buffer = await file.arrayBuffer();  // ArrayBuffer
const bytes = await file.bytes();         // Uint8Array
const stream = file.stream();             // ReadableStream

ファイルの書き込み (Bun.write()):

// 文字列を書き込み
await Bun.write("output.txt", "Hello, World!");

// ファイルコピー
await Bun.write(Bun.file("dest.txt"), Bun.file("src.txt"));

// バイナリデータ
await Bun.write("data.bin", new Uint8Array([1, 2, 3]));

// HTTP レスポンスをファイルに保存
const response = await fetch("https://example.com");
await Bun.write("page.html", response);

// stdout への書き込み
await Bun.write(Bun.stdout, Bun.file("data.txt"));

インクリメンタル書き込み (FileSink):

const file = Bun.file("log.txt");
const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB バッファ

writer.write("Line 1\n");
writer.write("Line 2\n");
writer.flush();  // バッファをディスクに書き出し

writer.end();    // バッファをフラッシュしてファイルを閉じる

ファイルの削除:

await Bun.file("temp.txt").delete();

7.2 SQLite(bun:sqlite)

Bun はネイティブ SQLite3 ドライバーを内蔵しており、better-sqlite3 の3〜6倍高速。

import { Database } from "bun:sqlite";

// データベースの作成/オープン
const db = new Database("mydb.sqlite");
// const db = new Database(":memory:");  // インメモリ
// const db = new Database("mydb.sqlite", { readonly: true });
// const db = new Database("mydb.sqlite", { create: true });
// const db = new Database("mydb.sqlite", { strict: true }); // 厳密モード

// WAL モード有効化(推奨)
db.run("PRAGMA journal_mode = WAL;");

// テーブル作成
db.run(`
  CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE
  )
`);

// プリペアドステートメント
const insert = db.query("INSERT INTO users (name, email) VALUES ($name, $email)");
insert.run({ $name: "Taro", $email: "taro@example.com" });

// クエリ
const users = db.query("SELECT * FROM users").all();
const user = db.query("SELECT * FROM users WHERE id = ?").get(1);
const values = db.query("SELECT name, email FROM users").values();

// イテレータ(大量データ向け)
for (const row of db.query("SELECT * FROM users").iterate()) {
  console.log(row);
}

// クラスへのマッピング
class User {
  name!: string;
  email!: string;
  get domain() {
    return this.email.split("@")[1];
  }
}
const typedUsers = db.query("SELECT name, email FROM users").as(User).all();
console.log(typedUsers[0].domain);

トランザクション:

const insertUser = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");

const insertMany = db.transaction((users: [string, string][]) => {
  for (const [name, email] of users) {
    insertUser.run(name, email);
  }
  return users.length;
});

// トランザクション実行(自動 BEGIN/COMMIT、エラー時 ROLLBACK)
const count = insertMany([
  ["Alice", "alice@example.com"],
  ["Bob", "bob@example.com"],
]);

// トランザクション種別
insertMany.deferred(data);    // BEGIN DEFERRED
insertMany.immediate(data);   // BEGIN IMMEDIATE
insertMany.exclusive(data);   // BEGIN EXCLUSIVE

ES モジュールインポート:

import db from "./mydb.sqlite" with { type: "sqlite" };
const result = db.query("SELECT * FROM users LIMIT 1").get();

using 文:

{
  using db = new Database("mydb.sqlite");
  using query = db.query("SELECT 'Hello' as message");
  console.log(query.get());
} // 自動的に close される

シリアライズ/デシリアライズ:

const contents = db.serialize();         // Uint8Array
const newDb = Database.deserialize(contents);

7.3 子プロセス(Bun.spawn)

// 非同期実行
const proc = Bun.spawn(["ls", "-la"], {
  cwd: "/tmp",
  env: { ...process.env, CUSTOM: "value" },
  stdout: "pipe",
  stderr: "inherit",
  onExit(proc, exitCode, signalCode, error) {
    console.log(`Exited with code ${exitCode}`);
  },
});

const output = await proc.stdout.text();
await proc.exited;  // 終了を待機

// 同期実行
const result = Bun.spawnSync(["echo", "hello"]);
console.log(result.stdout.toString()); // "hello\n"
console.log(result.success);           // true

// stdin パイプ
const cat = Bun.spawn(["cat"], { stdin: "pipe" });
cat.stdin.write("Hello ");
cat.stdin.write("World!");
cat.stdin.end();

// プロセスの強制終了
proc.kill();
proc.kill("SIGTERM");

// タイムアウト
const proc2 = Bun.spawn({
  cmd: ["sleep", "100"],
  timeout: 5000,         // 5秒後にキル
  killSignal: "SIGKILL",
});

// AbortSignal
const controller = new AbortController();
const proc3 = Bun.spawn({
  cmd: ["sleep", "100"],
  signal: controller.signal,
});
controller.abort();

// リソース使用量
await proc.exited;
const usage = proc.resourceUsage();
console.log(`Max RSS: ${usage.maxRSS} bytes`);
console.log(`CPU (user): ${usage.cpuTime.user} µs`);

IPC(プロセス間通信):

// parent.ts
const child = Bun.spawn(["bun", "child.ts"], {
  ipc(message, childProc) {
    console.log("From child:", message);
    childProc.send("Hi from parent!");
  },
  serialization: "advanced",  // または "json"(Node.js との通信時)
});
child.send("Hello child!");

// child.ts
process.on("message", (msg) => console.log("From parent:", msg));
process.send("Hello parent!");

ターミナル(PTY)サポート:

const proc = Bun.spawn(["bash"], {
  terminal: {
    cols: 80,
    rows: 24,
    data(terminal, data) {
      process.stdout.write(data);
    },
  },
});

proc.terminal.write("echo hello\n");
proc.terminal.resize(120, 40);
await proc.exited;
proc.terminal.close();

7.4 Bun Shell

Bun Shell は JavaScript から安全にシェルコマンドを実行するクロスプラットフォームの bash 互換シェルである。

import { $ } from "bun";

// 基本実行
await $`echo "Hello World!"`;

// 出力の取得
const text = await $`echo "Hello"`.text();
const json = await $`echo '{"a":1}'`.json();

// パイプ
await $`echo "Hello World!" | wc -w`;

// JavaScript オブジェクトとの連携
const response = new Response("hello body");
await $`cat < ${response} | wc -c`;

// ファイルリダイレクト
await $`echo "log entry" >> output.log`;
await $`bun run index.ts 2> errors.txt`;

// 環境変数
await $`FOO=bar bun -e 'console.log(process.env.FOO)'`;

// コマンド置換
await $`echo "Commit: $(git rev-parse HEAD)"`;

// エラーハンドリング
try {
  await $`failing-command`.text();
} catch (err) {
  console.log(`Exit code: ${err.exitCode}`);
  console.log(err.stderr.toString());
}

// 例外を投げない
const { exitCode, stdout } = await $`may-fail`.nothrow().quiet();

// 作業ディレクトリ
await $`pwd`.cwd("/tmp");

// グローバル設定
$.cwd("/tmp");
$.env({ ...process.env, NODE_ENV: "production" });
$.nothrow();  // 全コマンドで例外を投げない

// 行単位の処理
for await (const line of $`cat list.txt`.lines()) {
  console.log(line);
}

// セキュリティ: 入力は自動エスケープ
const userInput = "hello; rm -rf /";
await $`echo ${userInput}`;  // 安全(シェルインジェクション防止)

組み込みコマンド: cd, ls, rm, echo, pwd, cat, touch, mkdir, which, mv, exit, true, false, yes, seq, dirname, basename

.sh ファイルローダー:

# script.sh
echo "Hello from Bun Shell!"

# クロスプラットフォームで実行可能
bun ./script.sh

7.5 ハッシュ

// Bun.hash(非暗号学的、高速)
Bun.hash("hello");          // number
Bun.hash.wyhash("hello");   // wyhash
Bun.hash.adler32("hello");  // Adler-32
Bun.hash.crc32("hello");    // CRC-32
Bun.hash.cityHash32("hello");
Bun.hash.cityHash64("hello");
Bun.hash.murmur32v3("hello");
Bun.hash.murmur64v2("hello");

// 暗号学的ハッシュ
const hasher = new Bun.CryptoHasher("sha256");
hasher.update("hello");
const digest = hasher.digest("hex");

// パスワードハッシュ
const hash = await Bun.password.hash("mypassword", {
  algorithm: "bcrypt",  // または "argon2id"
  cost: 10,
});
const isValid = await Bun.password.verify("mypassword", hash);

7.6 その他の組み込み API

Glob:

const glob = new Bun.Glob("**/*.ts");
for await (const file of glob.scan({ cwd: "./src" })) {
  console.log(file);
}

Semver:

Bun.semver.satisfies("1.2.3", "^1.0.0");  // true
Bun.semver.order("1.0.0", "2.0.0");       // -1

Transpiler:

const transpiler = new Bun.Transpiler({
  loader: "tsx",
  target: "browser",
});

const code = transpiler.transformSync(`
  const App: React.FC = () => <div>Hello</div>;
  export default App;
`);

// インポートのスキャン
const imports = transpiler.scanImports(sourceCode);

Sleep:

await Bun.sleep(1000);  // 1秒待機

DNS:

const addresses = await Bun.dns.lookup("example.com");

peek(Promise のノンブロッキング読み取り):

import { peek } from "bun";
const promise = Promise.resolve(42);
const value = peek(promise);  // 42(すでに解決済みの場合)

8. 設定ファイル(bunfig.toml)

bunfig.toml は Bun 固有の設定ファイルである。オプションであり、Bun はデフォルト設定でそのまま動作する。

8.1 設定ファイルの検索順序

  1. $HOME/.bunfig.toml(グローバル)
  2. $XDG_CONFIG_HOME/.bunfig.toml(グローバル)
  3. ./bunfig.toml(ローカル、プロジェクトルート)

ローカルの設定がグローバル設定を上書きし、CLI フラグが全設定を上書きする。

8.2 完全な設定例

# ============================================================
# bunfig.toml - Bun 設定ファイル完全例
# ============================================================

# ---- ランタイム設定(トップレベル) ----

# 実行前に読み込むスクリプト(プラグイン登録など)
preload = ["./plugins/setup.ts"]

# JSX 設定(tsconfig.json でも可)
jsx = "react-jsx"
jsxFactory = "React.createElement"
jsxFragment = "React.Fragment"
jsxImportSource = "react"

# メモリ節約モード(GC頻度増加、パフォーマンス低下)
smol = false

# ログレベル: "debug" | "warn" | "error"
logLevel = "warn"

# テレメトリ(匿名クラッシュレポート)
telemetry = true

# .env ファイルの自動読み込み
# env = false  # 無効化する場合

# ---- コンパイル時置換 ----
[define]
"process.env.NODE_ENV" = "'production'"
"MY_CONSTANT" = "'hello'"

# ---- カスタムローダー ----
[loader]
".yaml" = "text"
".sql" = "text"
".graphql" = "text"

# ---- コンソール設定 ----
[console]
depth = 4  # console.log のオブジェクト展開深度(デフォルト: 2)

# ---- テストランナー設定 ----
[test]
root = "./__tests__"
preload = ["./test/setup.ts"]
timeout = 10000
retry = 2
coverage = true
coverageThreshold = { line = 0.8, function = 0.9 }
coverageSkipTestFiles = true
coverageReporter = ["text", "lcov"]
coverageDir = "coverage"
coveragePathIgnorePatterns = ["fixtures/**"]
randomize = false
# seed = 12345
# rerunEach = 3
# concurrentTestGlob = "**/concurrent-*.test.ts"
onlyFailures = false
pathIgnorePatterns = ["vendor/**", "node_modules/**"]

[test.reporter]
dots = false
# junit = "test-results.xml"

# ---- パッケージマネージャー設定 ----
[install]
optional = true
dev = true
peer = true
production = false
exact = false
frozenLockfile = false
dryRun = false
saveTextLockfile = true
auto = "auto"
linker = "hoisted"  # "hoisted" | "isolated"
concurrentScripts = 16
# minimumReleaseAge = 259200
# minimumReleaseAgeExcludes = ["@types/node"]

# レジストリ設定
registry = "https://registry.npmjs.org"
# registry = { url = "https://registry.npmjs.org", token = "xxx" }

# スコープ別レジストリ
[install.scopes]
# myorg = "https://registry.myorg.com/"
# myorg = { token = "$NPM_TOKEN", url = "https://registry.myorg.com/" }

# キャッシュ設定
[install.cache]
dir = "~/.bun/install/cache"
disable = false
disableManifest = false

# ロックファイル設定
[install.lockfile]
save = true
# print = "yarn"  # yarn.lock も生成

# セキュリティスキャナー
# [install.security]
# scanner = "@acme/bun-security-scanner"

# ---- bun run 設定 ----
[run]
shell = "system"   # "system"(デフォルト)| "bun"(Windows デフォルト)
bun = false        # true: node コマンドを bun にエイリアス
silent = false     # true: コマンド出力を抑制

# ---- デバッグ設定 ----
[debug]
editor = "code"  # "vscode" | "subl" | "nvim" | "vim" | "emacs" | "idea"

9. Node.js 互換性

9.1 対応状況サマリー

Bun は Node.js v23 との互換性を目指し、毎リリース前に Node.js のテストスイートから数千のテストを実行している。

完全対応(🟢)のモジュール:

モジュールテストパス率
node:assert100%
node:buffer100%
node:console100%
node:events100%
node:fs92%
node:os100%
node:path100%
node:querystring100%
node:string_decoder100%
node:url100%
node:zlib98%
node:http完全実装
node:net完全実装
node:stream完全実装
node:readline完全実装
node:dns>90%
node:dgram>90%
node:diagnostics_channel完全実装

部分対応(🟡)のモジュール:

モジュール備考
node:cryptosetEngine, setFips 未実装
node:child_processIPC でのソケットハンドル送信未対応
node:http2gRPC テスト95.25%パス
node:worker_threads一部オプション未サポート
node:vmコア機能は実装済み
node:async_hooksAsyncLocalStorage 実装済み
node:tlscreateSecurePair 未実装

未実装(🔴)のモジュール: node:repl, node:trace_events

9.2 グローバル変数の互換性

ほぼ全てのグローバル変数が完全実装されている: Buffer, process, console, fetch, URL, WebSocket, ReadableStream, Request, Response, Headers, FormData, TextEncoder, TextDecoder, crypto, setTimeout, setInterval, queueMicrotask, structuredClone, Blob, EventTarget, AbortController, BroadcastChannel, MessageChannel 等。

process は大部分が実装済みだが、process.binding(内部バインディング)は部分的、process.title は macOS/Linux でノーオプ。

9.3 互換性に関する方針

Node.js で動作するパッケージが Bun で動作しない場合、それは Bun のバグとみなされる。

10. プラグインシステム

10.1 プラグインの構造

import type { BunPlugin } from "bun";

const myPlugin: BunPlugin = {
  name: "My Plugin",
  setup(build) {
    // ライフサイクルフックの登録
    build.onStart(() => {
      console.log("Build started!");
    });

    build.onResolve({ filter: /^virtual:/ }, (args) => {
      return { path: args.path, namespace: "virtual" };
    });

    build.onLoad({ filter: /.*/, namespace: "virtual" }, (args) => {
      return {
        contents: `export default "Virtual module: ${args.path}"`,
        loader: "js",
      };
    });
  },
};

10.2 ランタイムプラグイン

// preload.ts(bunfig.toml の preload で指定)
import { plugin } from "bun";

plugin({
  name: "SCSS loader",
  setup(build) {
    build.onLoad({ filter: /\.scss$/ }, async (args) => {
      const sass = require("sass");
      const result = sass.compile(args.path);
      return {
        contents: `
          const style = document.createElement("style");
          style.textContent = ${JSON.stringify(result.css)};
          document.head.appendChild(style);
        `,
        loader: "js",
      };
    });
  },
});

10.3 バンドラープラグイン

await Bun.build({
  entrypoints: ["./src/index.ts"],
  outdir: "./dist",
  plugins: [
    {
      name: "Environment plugin",
      setup(build) {
        build.onLoad({ filter: /^env$/ }, () => ({
          contents: `export default ${JSON.stringify(process.env)}`,
          loader: "js",
        }));
      },
    },
  ],
});

10.4 defer によるプラグインの遅延実行

plugin({
  name: "Import tracker",
  setup(build) {
    const tracked: Record<string, number> = {};

    build.onLoad({ filter: /\.ts$/ }, async ({ path }) => {
      const transpiler = new Bun.Transpiler();
      const contents = await Bun.file(path).arrayBuffer();
      for (const imp of transpiler.scanImports(contents)) {
        tracked[imp.path] = (tracked[imp.path] || 0) + 1;
      }
      return undefined;
    });

    build.onLoad({ filter: /stats\.json/ }, async ({ defer }) => {
      await defer(); // 他の全モジュールの読み込みを待つ
      return {
        contents: JSON.stringify(tracked),
        loader: "json",
      };
    });
  },
});

11. 実践的なプロジェクト構成例

11.1 Web アプリケーション(フルスタック)

my-web-app/
├── bunfig.toml
├── package.json
├── tsconfig.json
├── public/
│   ├── index.html
│   └── favicon.ico
├── src/
│   ├── server.ts          # Bun.serve エントリポイント
│   ├── routes/
│   │   ├── api.ts
│   │   └── pages.ts
│   ├── db/
│   │   ├── schema.ts
│   │   └── queries.ts
│   └── client/
│       ├── index.tsx
│       └── components/
├── test/
│   ├── setup.ts
│   ├── api.test.ts
│   └── db.test.ts
└── scripts/
    ├── seed.ts
    └── migrate.ts
// package.json
{
  "name": "my-web-app",
  "scripts": {
    "dev": "bun --hot run src/server.ts",
    "build": "bun build src/client/index.tsx --outdir ./dist --minify",
    "start": "NODE_ENV=production bun run src/server.ts",
    "test": "bun test",
    "test:coverage": "bun test --coverage",
    "db:seed": "bun run scripts/seed.ts",
    "db:migrate": "bun run scripts/migrate.ts"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@types/bun": "latest",
    "@happy-dom/global-registrator": "latest"
  }
}
# bunfig.toml
[test]
preload = ["./test/setup.ts"]
coverage = true
coverageThreshold = 0.8

[run]
bun = true

11.2 サーバー実装例

// src/server.ts
import { Database } from "bun:sqlite";
import homepage from "./client/index.html";

const db = new Database("app.db");
db.run("PRAGMA journal_mode = WAL;");

Bun.serve({
  port: process.env.PORT || 3000,

  routes: {
    // フロントエンド(HTML インポートで HMR 対応)
    "/": homepage,

    // API エンドポイント
    "/api/users": {
      GET: () => {
        const users = db.query("SELECT * FROM users").all();
        return Response.json(users);
      },
      POST: async (req) => {
        const { name, email } = await req.json();
        const id = crypto.randomUUID();
        db.query("INSERT INTO users (id, name, email) VALUES (?, ?, ?)")
          .run(id, name, email);
        return Response.json({ id, name, email }, { status: 201 });
      },
    },

    "/api/users/:id": {
      GET: (req) => {
        const user = db.query("SELECT * FROM users WHERE id = ?")
          .get(req.params.id);
        return user
          ? Response.json(user)
          : Response.json({ error: "Not found" }, { status: 404 });
      },
    },

    // 静的ファイル
    "/favicon.ico": Bun.file("./public/favicon.ico"),
  },

  // WebSocket
  websocket: {
    open(ws) { ws.subscribe("notifications"); },
    message(ws, msg) { ws.publish("notifications", msg); },
    close(ws) { ws.unsubscribe("notifications"); },
  },

  error(error) {
    console.error(error);
    return Response.json(
      { error: "Internal Server Error" },
      { status: 500 }
    );
  },
});

11.3 モノレポ構成例

my-monorepo/
├── bunfig.toml
├── package.json
├── packages/
│   ├── shared/
│   │   ├── package.json
│   │   └── src/
│   ├── api/
│   │   ├── package.json
│   │   └── src/
│   └── web/
│       ├── package.json
│       └── src/
└── apps/
    └── dashboard/
        ├── package.json
        └── src/
// package.json(ルート)
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "scripts": {
    "dev": "bun run --filter '*' dev",
    "build": "bun run --filter '*' build",
    "test": "bun run --filter '*' test",
    "lint": "bun run --parallel lint:api lint:web"
  }
}
# bunfig.toml
[install]
linker = "isolated"   # モノレポではファントム依存関係を防止

12. パフォーマンスとベンチマーク

12.1 起動速度

コマンド実行時間
bun hello.js5.2ms
node hello.js25.1ms
倍率約4倍高速

12.2 パッケージマネージャー

操作npmBun
install(キャッシュなし)~35秒~1.4秒
倍率-約25倍高速

12.3 HTTP サーバー

ランタイムリクエスト/秒
Node.js 16~64,000
Bun~160,000
倍率約2.5倍

12.4 SQLite

ドライバー相対速度
bun:sqlite1x(基準)
better-sqlite33〜6倍遅い
deno.land/x/sqlite8〜9倍遅い

12.5 バンドラー

esbuild の three.js ベンチマークにおいて、Bun のバンドラーは esbuild より約1.75倍高速。

12.6 ファイル I/O

Bun.write による cat コマンドの実装は、GNU cat の2倍高速(Linux、大きなファイル)。

13. まとめ

13.1 Bun の強み

  1. 圧倒的な速度: JavaScriptCore エンジンと Zig による実装で、起動・実行・インストール全てが高速
  2. オールインワン: ランタイム、パッケージマネージャー、テストランナー、バンドラーを単一バイナリに統合
  3. TypeScript ネイティブ: 設定不要で TypeScript/JSX を直接実行
  4. Node.js 互換: 既存の Node.js プロジェクトにほぼそのまま導入可能
  5. Web 標準準拠: fetch, WebSocket, Response 等の Web API をネイティブサポート
  6. 開発体験: ホットリロード、組み込みデバッガー、自動インストール等
  7. 組み込みデータベース: SQLite のネイティブサポート
  8. セキュリティ: ライフサイクルスクリプトの制限、Bun Shell の自動エスケープ

13.2 適用場面

場面推奨度理由
新規 Web アプリケーション★★★★★全ツールチェーンが統合
既存 Node.js プロジェクトの高速化★★★★☆ドロップイン代替として導入可能
CLI ツール開発★★★★★高速起動、単一バイナリ配布
API サーバー★★★★★Bun.serve の高性能
モノレポ★★★★☆ワークスペース + isolated install
CI/CD パイプライン★★★★☆bun ci + 高速インストール

13.3 今後の展望

Bun は活発に開発が続けられており、Node.js との100%互換性を目標としている。コミュニティも急速に成長しており、エコシステムの充実が進んでいる。JavaScript/TypeScript 開発の新たなスタンダードとなるポテンシャルを持つツールキットである。