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) - 両形式の混在も可能
解決順序:
package.jsonの"exports"フィールドpackage.jsonの"main"フィールド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 としてパース
},
});
対応ローダー一覧:
| ローダー | 説明 |
|---|---|
js | JavaScript |
jsx | JSX を含む JavaScript |
ts | TypeScript |
tsx | JSX を含む TypeScript |
json | JSON |
toml | TOML |
text | テキスト(文字列として export) |
file | ファイル(URLとして export) |
wasm | WebAssembly |
napi | Node-API ネイティブモジュール |
css | CSS |
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 設定ファイルの検索順序
$HOME/.bunfig.toml(グローバル)$XDG_CONFIG_HOME/.bunfig.toml(グローバル)./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:assert | 100% |
node:buffer | 100% |
node:console | 100% |
node:events | 100% |
node:fs | 92% |
node:os | 100% |
node:path | 100% |
node:querystring | 100% |
node:string_decoder | 100% |
node:url | 100% |
node:zlib | 98% |
node:http | 完全実装 |
node:net | 完全実装 |
node:stream | 完全実装 |
node:readline | 完全実装 |
node:dns | >90% |
node:dgram | >90% |
node:diagnostics_channel | 完全実装 |
部分対応(🟡)のモジュール:
| モジュール | 備考 |
|---|---|
node:crypto | setEngine, setFips 未実装 |
node:child_process | IPC でのソケットハンドル送信未対応 |
node:http2 | gRPC テスト95.25%パス |
node:worker_threads | 一部オプション未サポート |
node:vm | コア機能は実装済み |
node:async_hooks | AsyncLocalStorage 実装済み |
node:tls | createSecurePair 未実装 |
未実装(🔴)のモジュール: 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.js | 5.2ms |
node hello.js | 25.1ms |
| 倍率 | 約4倍高速 |
12.2 パッケージマネージャー
| 操作 | npm | Bun |
|---|---|---|
install(キャッシュなし) | ~35秒 | ~1.4秒 |
| 倍率 | - | 約25倍高速 |
12.3 HTTP サーバー
| ランタイム | リクエスト/秒 |
|---|---|
| Node.js 16 | ~64,000 |
| Bun | ~160,000 |
| 倍率 | 約2.5倍 |
12.4 SQLite
| ドライバー | 相対速度 |
|---|---|
bun:sqlite | 1x(基準) |
better-sqlite3 | 3〜6倍遅い |
deno.land/x/sqlite | 8〜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 の強み
- 圧倒的な速度: JavaScriptCore エンジンと Zig による実装で、起動・実行・インストール全てが高速
- オールインワン: ランタイム、パッケージマネージャー、テストランナー、バンドラーを単一バイナリに統合
- TypeScript ネイティブ: 設定不要で TypeScript/JSX を直接実行
- Node.js 互換: 既存の Node.js プロジェクトにほぼそのまま導入可能
- Web 標準準拠:
fetch,WebSocket,Response等の Web API をネイティブサポート - 開発体験: ホットリロード、組み込みデバッガー、自動インストール等
- 組み込みデータベース: SQLite のネイティブサポート
- セキュリティ: ライフサイクルスクリプトの制限、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 開発の新たなスタンダードとなるポテンシャルを持つツールキットである。