Bun完全ガイド2026 — 高速JavaScriptランタイム & オールインワンツール


Bunは、2026年現在、最も注目されているJavaScriptランタイムの一つです。Node.jsやDenoの代替として、高速なパッケージマネージャー、バンドラー、テストランナーを統合し、開発体験を大幅に向上させます。この記事では、Bunの全機能を実践的に解説します。

Bunとは

Bunは、以下の特徴を持つオールインワンJavaScriptツールです。

  • 高速ランタイム: JavaScriptCoreエンジンを使用、Node.jsより3倍高速
  • パッケージマネージャー: npm/yarn/pnpmより10-100倍高速
  • バンドラー: esbuild/webpackより高速なビルド
  • テストランナー: Jest互換のテストフレームワーク
  • TypeScript/JSXサポート: トランスパイル不要で実行可能
  • Node.js互換: 既存のnpmパッケージをそのまま使用可能

インストール

macOS/Linux

# インストール
curl -fsSL https://bun.sh/install | bash

# バージョン確認
bun --version

Windows

# PowerShellで実行
powershell -c "irm bun.sh/install.ps1 | iex"

# または、WSL経由
wsl curl -fsSL https://bun.sh/install | bash

Dockerで使用

FROM oven/bun:1

WORKDIR /app

COPY package.json bun.lockb ./
RUN bun install

COPY . .

CMD ["bun", "run", "index.ts"]

Bunランタイム

TypeScript/JSXを直接実行

# TypeScriptファイルを実行(トランスパイル不要)
bun run index.ts

# JSXファイルを実行
bun run app.tsx

# watchモード
bun --watch index.ts

# hotモード(変更時に自動リロード)
bun --hot index.ts

基本的な使い方

// index.ts
console.log('Hello from Bun!');

// TypeScriptの型チェック
const greeting: string = 'Hello';
console.log(greeting);

// トップレベルawait
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);

実行:

bun run index.ts

HTTPサーバー

Bunは非常に高速なHTTPサーバーを内蔵しています。

// server.ts
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);

    if (url.pathname === '/') {
      return new Response('Hello Bun!');
    }

    if (url.pathname === '/json') {
      return Response.json({ message: 'Hello', timestamp: Date.now() });
    }

    return new Response('Not Found', { status: 404 });
  },
});

console.log(`Listening on http://localhost:${server.port}`);
bun run server.ts

ファイル操作

Bunは高速なファイルAPIを提供します。

// ファイル読み込み
const file = Bun.file('data.json');
const text = await file.text();
const json = await file.json();

// ファイル書き込み
await Bun.write('output.txt', 'Hello Bun!');
await Bun.write('data.json', JSON.stringify({ name: 'Bun' }));

// ストリーム
const stream = Bun.file('large-file.txt').stream();
for await (const chunk of stream) {
  console.log(chunk);
}

環境変数

// .env.local
// API_KEY=secret123

// 環境変数を読み込み(自動)
console.log(Bun.env.API_KEY); // 'secret123'
console.log(process.env.API_KEY); // 'secret123'

// .env.productionを読み込み
import { env } from 'bun';
console.log(env.NODE_ENV);

パッケージマネージャー

プロジェクト初期化

# 新しいプロジェクト作成
bun init

# package.jsonが生成される

パッケージインストール

# package.jsonからインストール
bun install

# パッケージを追加
bun add react react-dom
bun add -d @types/react @types/react-dom

# グローバルインストール
bun add -g typescript

# 特定バージョン
bun add react@18.2.0

# devDependenciesに追加
bun add -d prettier eslint

パッケージ削除

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

# 未使用パッケージをクリーンアップ
bun pm cache rm

パッケージアップデート

# すべてのパッケージを最新に
bun update

# 特定パッケージのみ
bun update react react-dom

速度比較

npm install:     45秒
yarn install:    30秒
pnpm install:    15秒
bun install:     0.5秒(90倍高速)

bun.lockb

Bunは独自のバイナリロックファイル bun.lockb を使用します。

# ロックファイルを更新
bun install --force

# ロックファイルを再生成
rm bun.lockb
bun install

スクリプト実行

package.jsonのscripts

{
  "scripts": {
    "dev": "bun run --watch src/index.ts",
    "build": "bun build src/index.ts --outdir ./dist",
    "test": "bun test",
    "start": "bun run dist/index.js"
  }
}
# スクリプト実行
bun run dev
bun run build
bun test

# または短縮形
bun dev
bun build

環境変数を渡す

# 環境変数を設定して実行
NODE_ENV=production bun run start

# .envファイルを指定
bun --env-file=.env.production run start

バンドラー

基本的なビルド

# シングルファイルにバンドル
bun build ./src/index.ts --outdir ./dist

# minify
bun build ./src/index.ts --outdir ./dist --minify

# sourcemap生成
bun build ./src/index.ts --outdir ./dist --sourcemap=external

エントリーポイント複数

# 複数のエントリーポイント
bun build ./src/index.ts ./src/worker.ts --outdir ./dist

ターゲット指定

# ブラウザ向け
bun build ./src/index.ts --outdir ./dist --target browser

# Node.js向け
bun build ./src/index.ts --outdir ./dist --target node

# Bun向け
bun build ./src/index.ts --outdir ./dist --target bun

プログラマティックビルド

// build.ts
await Bun.build({
  entrypoints: ['./src/index.ts'],
  outdir: './dist',
  minify: true,
  sourcemap: 'external',
  target: 'browser',
  splitting: true, // コード分割
  external: ['react', 'react-dom'], // バンドルから除外
});

React/Next.jsのビルド

// React コンポーネント
// App.tsx
import React from 'react';

export default function App() {
  return (
    <div>
      <h1>Hello Bun + React</h1>
    </div>
  );
}
# Reactプロジェクトをビルド
bun build ./src/App.tsx --outdir ./dist --target browser --minify

バンドルサイズの最適化

await Bun.build({
  entrypoints: ['./src/index.ts'],
  outdir: './dist',
  minify: {
    whitespace: true,
    identifiers: true,
    syntax: true,
  },
  naming: '[dir]/[name]-[hash].[ext]', // ファイル名にハッシュを追加
});

テストランナー

基本的なテスト

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

describe('Math operations', () => {
  test('addition', () => {
    expect(1 + 1).toBe(2);
  });

  test('subtraction', () => {
    expect(5 - 3).toBe(2);
  });

  test('multiplication', () => {
    expect(2 * 3).toBe(6);
  });
});
# テスト実行
bun test

# watchモード
bun test --watch

# 特定ファイルのみ
bun test math.test.ts

# カバレッジ
bun test --coverage

非同期テスト

// async.test.ts
import { expect, test } from 'bun:test';

test('async fetch', async () => {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  expect(data).toBeDefined();
});

test('timeout', async () => {
  await new Promise(resolve => setTimeout(resolve, 100));
  expect(true).toBe(true);
}, { timeout: 200 });

モック

// mock.test.ts
import { expect, test, mock } from 'bun:test';

test('mock function', () => {
  const mockFn = mock((a: number, b: number) => a + b);

  mockFn(1, 2);
  mockFn(3, 4);

  expect(mockFn).toHaveBeenCalledTimes(2);
  expect(mockFn).toHaveBeenCalledWith(1, 2);
  expect(mockFn).toHaveBeenCalledWith(3, 4);
});

test('mock module', async () => {
  mock.module('./utils', () => ({
    add: (a: number, b: number) => a + b + 1, // モック実装
  }));

  const { add } = await import('./utils');
  expect(add(1, 2)).toBe(4); // モックされた値
});

スナップショットテスト

// snapshot.test.ts
import { expect, test } from 'bun:test';

test('snapshot', () => {
  const data = {
    name: 'Bun',
    version: '1.0.0',
    features: ['fast', 'all-in-one'],
  };

  expect(data).toMatchSnapshot();
});

実践例

REST API

// api/server.ts
const server = Bun.serve({
  port: 3000,

  async fetch(req) {
    const url = new URL(req.url);

    // CORS
    if (req.method === 'OPTIONS') {
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
        },
      });
    }

    // GET /api/users
    if (url.pathname === '/api/users' && req.method === 'GET') {
      const users = await db.getAllUsers();
      return Response.json(users);
    }

    // POST /api/users
    if (url.pathname === '/api/users' && req.method === 'POST') {
      const body = await req.json();
      const user = await db.createUser(body);
      return Response.json(user, { status: 201 });
    }

    // GET /api/users/:id
    const userMatch = url.pathname.match(/^\/api\/users\/(\d+)$/);
    if (userMatch && req.method === 'GET') {
      const userId = parseInt(userMatch[1]);
      const user = await db.getUserById(userId);
      return user ? Response.json(user) : new Response('Not Found', { status: 404 });
    }

    return new Response('Not Found', { status: 404 });
  },
});

console.log(`Server running on http://localhost:${server.port}`);

WebSocketサーバー

// websocket.ts
const server = Bun.serve({
  port: 3000,

  fetch(req, server) {
    if (server.upgrade(req)) {
      return; // WebSocketにアップグレード
    }
    return new Response('WebSocket server');
  },

  websocket: {
    open(ws) {
      console.log('Client connected');
      ws.send('Welcome!');
    },

    message(ws, message) {
      console.log('Received:', message);
      ws.send(`Echo: ${message}`);
    },

    close(ws) {
      console.log('Client disconnected');
    },
  },
});

console.log(`WebSocket server running on ws://localhost:${server.port}`);

クライアント側:

const ws = new WebSocket('ws://localhost:3000');

ws.onopen = () => {
  console.log('Connected');
  ws.send('Hello server!');
};

ws.onmessage = (event) => {
  console.log('Message:', event.data);
};

SQLiteデータベース

// db.ts
import { Database } from 'bun:sqlite';

const db = new Database('mydb.sqlite');

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

// INSERT
const insertStmt = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
insertStmt.run('John Doe', 'john@example.com');

// SELECT
const selectStmt = db.prepare('SELECT * FROM users WHERE id = ?');
const user = selectStmt.get(1);
console.log(user);

// SELECT ALL
const allUsers = db.query('SELECT * FROM users').all();
console.log(allUsers);

// トランザクション
const transaction = db.transaction(() => {
  insertStmt.run('Alice', 'alice@example.com');
  insertStmt.run('Bob', 'bob@example.com');
});

transaction(); // トランザクション実行

db.close();

ファイルアップロード

// upload-server.ts
const server = Bun.serve({
  port: 3000,

  async fetch(req) {
    if (req.method === 'POST' && req.url.endsWith('/upload')) {
      const formData = await req.formData();
      const file = formData.get('file') as File;

      if (!file) {
        return new Response('No file uploaded', { status: 400 });
      }

      // ファイルを保存
      await Bun.write(`uploads/${file.name}`, file);

      return Response.json({
        message: 'File uploaded successfully',
        filename: file.name,
        size: file.size,
      });
    }

    return new Response('Upload endpoint: POST /upload');
  },
});

スケジューラー(Cron)

// scheduler.ts
async function runTask() {
  console.log('Task running at', new Date().toISOString());
  // タスク処理
}

// 10秒ごとに実行
setInterval(runTask, 10000);

// 毎日午前2時に実行(外部ライブラリ使用)
import cron from 'node-cron';

cron.schedule('0 2 * * *', () => {
  console.log('Daily task running');
  runTask();
});

パフォーマンス比較

ランタイム速度

ベンチマーク: HTTP リクエスト処理
Node.js: 50,000 req/s
Deno:    60,000 req/s
Bun:     150,000 req/s (3倍高速)

起動時間

Node.js: 150ms
Deno:    120ms
Bun:     3ms (50倍高速)

パッケージインストール

npm:  45秒
yarn: 30秒
pnpm: 15秒
Bun:  0.5秒

制限事項と注意点

Node.js互換性

Bunは多くのNode.js APIをサポートしていますが、一部未対応のAPIがあります。

// サポートされているAPI
import fs from 'fs';
import path from 'path';
import http from 'http';
import crypto from 'crypto';

// 一部未対応のAPI
// - child_process(一部)
// - clusterモジュール
// - 一部のネイティブモジュール

ブラウザAPIの制限

Bunはサーバーサイドランタイムなので、一部のブラウザAPIは使用できません。

TypeScript設定

// tsconfig.json
{
  "compilerOptions": {
    "types": ["bun-types"],
    "lib": ["ESNext"],
    "module": "esnext",
    "target": "esnext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "strict": true
  }
}

まとめ

Bunの主な特徴:

利点:

  • 圧倒的な高速性(Node.jsの3倍)
  • オールインワンツール(ランタイム + パッケージマネージャー + バンドラー + テストランナー)
  • TypeScript/JSXのネイティブサポート
  • Node.js互換性
  • シンプルなAPI

適しているケース:

  • 新規プロジェクト
  • パフォーマンスが重要なAPI
  • 開発効率を重視する場合
  • モノレポ環境

注意点:

  • Node.js完全互換ではない
  • エコシステムはまだ発展途上
  • 本番環境での実績は少ない

Bunは、2026年現在、最も革新的なJavaScriptツールの一つです。開発体験とパフォーマンスを両立させたい開発者に最適です。