Tasuke Hubのロゴ

ITを中心に困っている人を助けるメディア

分かりやすく解決策を提供することで、あなたの困ったをサポート。 全ての人々がスムーズに生活できる世界を目指します。

【2025年最新】Bunで爆速JavaScript開発!従来の3倍速いランタイムの基本と活用法

記事のサムネイル
TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者

Bunとは?JavaScriptランタイムの新たな選択肢

Bunとは、JavaScriptとTypeScriptを高速に実行するためのオールインワンツールキットです。JavaScript開発に必要なランタイム、バンドラー、テストランナー、パッケージマネージャを1つにまとめたツールで、2021年に登場して以来、Web開発者から大きな注目を集めています。

Bunの最大の特徴は、その圧倒的な実行速度です。Node.jsと比較して最大3倍の速度を実現し、開発者の生産性を大幅に向上させます。また、JavaScriptだけでなくTypeScriptもネイティブにサポートしているため、トランスパイル不要で直接実行できます。

# 基本的な使い方
bun run index.ts  # TypeScriptファイルを直接実行

Bunはサーバーサイドのコードを書く際にも強力なサポートを提供し、特にAPIサーバーやマイクロサービスの構築に適しています。Node.jsと高い互換性を持ちながらも、よりモダンで効率的なAPIを提供している点が魅力です。

多くの一般的なNode.jsパッケージはBunでもそのまま動作するため、既存のプロジェクトからの移行も比較的容易です。ただし、あらゆるNode.jsモジュールとの完全な互換性はまだ実現されていないため、使用する際は互換性の確認が重要です。

「なぜもう一つのJavaScriptランタイムが必要なのか?」と疑問に思う方もいるでしょう。しかし、Bunは単なる代替ツールではなく、現代のWeb開発の課題を解決するために最初から設計されています。特に開発環境の起動時間の短縮、ビルドプロセスの高速化、APIレスポンスの改善などの点で大きなメリットをもたらします。

おすすめの書籍

Nodeよりも速い!Bunが高性能な理由を解説

Bunが従来のNode.jsと比較して3倍以上高速である秘密は、その基盤技術と設計思想にあります。なぜBunはここまで高速なのか、その理由を技術的観点から見ていきましょう。

高速化の基盤技術:JavaScriptCoreエンジン

Bunの最大の特徴は、Node.jsやDenoで使用されているV8エンジンではなく、Appleが開発したJavaScriptCoreエンジン(Safariと同じエンジン)を採用している点です。JavaScriptCoreはメモリ消費が少なく、特に起動時間が短いという特徴があります。

// 実行速度の比較例
// Node.js
// $ time node -e "console.log('Hello World')"
// 実行時間: 約70-100ms

// Bun
// $ time bun -e "console.log('Hello World')"
// 実行時間: 約5-10ms

Zigプログラミング言語による実装

BunはJavaScriptやC++ではなく、メモリ安全性と高いパフォーマンスを両立する新しいプログラミング言語「Zig」で実装されています。Zigは低レベルな最適化が可能でありながら、安全性も確保されているため、Bunの内部実装に理想的な言語です。

最初からパフォーマンスを重視した設計

Bunは後方互換性や歴史的な制約よりも、パフォーマンスを最優先する設計思想で開発されています。例えば:

  • 非同期I/Oの最適化
  • JavaScriptモジュール解決の高速化
  • 内部キャッシュの徹底活用
  • 起動時のプリロードの最小化

ファイルシステムへのアクセス最適化

ファイルの読み書きは多くのWeb開発で頻繁に行われる操作ですが、Bunではこの部分が特に最適化されています。

// Node.jsの場合
const fs = require('fs/promises');
await fs.readFile('file.txt', 'utf8');

// Bunの場合
const text = await Bun.file('file.txt').text();
// より簡潔で高速

内蔵トランスパイラとバンドラー

Bunには高速なトランスパイラとバンドラーが内蔵されており、外部ツールに依存せずに直接TypeScriptやJSXを処理できます。これにより、開発中の変更反映も非常に高速です。

これらの技術的特徴により、Bunは特に以下のようなケースで顕著な速度向上を実現しています:

  • 開発サーバーの起動時間(Viteより最大30倍高速)
  • APIサーバーのレスポンス時間
  • 大規模プロジェクトのビルド時間
  • TypeScriptファイルの直接実行

ただし、すべての面でBunが優れているわけではありません。特に長時間動作するプロセスや非常に複雑な計算を行う場合には、V8エンジンの方が最適化の恩恵を受けられる場合もあります。用途に応じて適切に選択することが重要です。

おすすめの書籍

Bunインストールから初めての実行まで

Bunを使い始めるには、まずお使いのシステムにインストールする必要があります。ここでは、主要なOSへのインストール方法と基本的な使い方を紹介します。

Bunのインストール

Bunは以下のOSをサポートしています:

  • macOS (x64, ARM)
  • Linux (x64, ARM)
  • Windows (WSL経由)

macOSへのインストール

macOSでは、curlコマンドを使った一行インストールが最も簡単です。

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

Homebrewパッケージマネージャーからもインストールできます。

brew tap oven-sh/bun
brew install bun

Linuxへのインストール

Linuxでも、curlを使ったインストールが可能です。

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

Windowsへのインストール

Windowsでは、WSL (Windows Subsystem for Linux) を使用してBunを実行します。

  1. まずWSLをインストールします。

    wsl --install
  2. WSL内でBunをインストールします。

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

インストールの確認

インストールが完了したら、以下のコマンドでBunのバージョンを確認できます。

bun --version

最新版が表示されれば、インストールは成功です。

最初のBunプロジェクト

新しいBunプロジェクトを始めるには、以下のコマンドを実行します。

mkdir my-bun-project
cd my-bun-project
bun init

bun initコマンドを実行すると、対話形式で基本的なプロジェクト設定が行われます。すべてデフォルトでもOKですし、お好みに合わせて設定を変更することもできます。

最初のTypeScriptファイルの作成と実行

Bunの最大の特徴の一つは、TypeScriptファイルをネイティブに実行できることです。以下のようにTypeScriptファイルを作成してみましょう。

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

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

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

このファイルを実行するには、次のコマンドを使います。

bun run index.ts

これだけで、TypeScriptファイルが直接実行され、簡単なWebサーバーが起動します。ブラウザでhttp://localhost:3000にアクセスすると、「Welcome to Bun!」というメッセージが表示されます。

基本的なBunコマンド

Bunには多くの機能が含まれていますが、以下は日常的によく使う基本コマンドです。

# JavaScriptまたはTypeScriptファイルを実行
bun run <ファイル名>

# package.jsonのscriptsを実行
bun run <スクリプト名>

# パッケージをインストール
bun install <パッケージ名>

# 開発サーバーを起動(hot reloadingあり)
bun --hot <ファイル名>

# テストを実行
bun test

Bunは非常に高速なだけでなく、シンプルで直感的なAPIを提供しているため、初めての方でも短時間で基本的な使い方を習得できます。次のセクションでは、Bunのパッケージマネージャーとしての機能を詳しく見ていきましょう。

おすすめの書籍

パッケージマネージャとしてのBunの使い方

Bunはランタイムだけでなく、高速なパッケージマネージャとしても機能します。npmやyarnの代わりとして使用でき、互換性を保ちながらパフォーマンスを大幅に向上させています。ここでは、Bunをパッケージマネージャとして使用する方法を見ていきましょう。

パッケージのインストール

基本的なパッケージのインストールは、bun installコマンドを使用します。

# 単一パッケージのインストール
bun install express

# 複数パッケージのインストール
bun install express mongoose dotenv

# 開発用パッケージのインストール
bun install --dev typescript @types/express

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

Bunのパッケージインストールは従来のnpmやyarnと比較して非常に高速です。特に依存関係が多い大規模プロジェクトでは、その差が顕著になります。

package.jsonとの互換性

Bunは標準的なpackage.jsonファイルをサポートしており、既存のNode.jsプロジェクトをそのまま使うことができます。

{
  "name": "my-bun-project",
  "version": "1.0.0",
  "module": "index.ts",
  "type": "module",
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "typescript": "^5.0.0"
  },
  "scripts": {
    "start": "bun run index.ts",
    "dev": "bun --hot run index.ts",
    "test": "bun test"
  }
}

既存のpackage.jsonを持つプロジェクトでbun installを実行すると、Bunは自動的にすべての依存関係をインストールします。

bun.lockb - バイナリロックファイル

Bunは依存関係をトラッキングするために、bun.lockbという独自のバイナリロックファイルを生成します。このファイルはテキストベースの従来のロックファイル(package-lock.jsonやyarn.lock)と比較して、はるかに小さく、解析も高速です。

# ロックファイルの内容を表示
bun pm inspect

スクリプトの実行

package.jsonのscriptsセクションに定義されたスクリプトは、bun runで実行できます。

# package.jsonのstartスクリプトを実行
bun run start

# テストを実行
bun run test

依存関係の管理

# プロジェクトの依存関係を更新
bun update

# 使用していない依存関係を検出
bun pm ls --unused

# 依存関係のセキュリティ脆弱性をチェック
bun pm audit

monorepoのサポート

Bunはmonorepo(複数のパッケージを含む単一リポジトリ)をサポートしており、workspacesを使用して複数のパッケージを効率的に管理できます。

// package.json
{
  "name": "my-monorepo",
  "workspaces": [
    "packages/*"
  ]
}

特定のワークスペースでコマンドを実行するには:

# 特定のワークスペースでパッケージをインストール
bun install --cwd packages/my-package express

# 特定のワークスペースでスクリプトを実行
bun run --cwd packages/my-package start

npmとyarnとの比較

Bunのパッケージマネージャは、npmやyarnと比較して以下のような利点があります:

  1. 速度: 並列処理とバイナリロックファイルにより、インストールと解決が非常に高速
  2. ディスク容量: より効率的なキャッシング戦略により、ディスク使用量を削減
  3. シンプルさ: 直感的なAPIと少ないコマンドセット
  4. ネイティブなTypeScriptサポート: .tsファイルを直接実行可能

ただし、すべてのnpmパッケージが完全に互換性があるわけではないため、特に複雑な依存関係やネイティブモジュールを使用するプロジェクトでは注意が必要です。

Bunのパッケージマネージャは日々進化しており、将来的にはさらに多くの機能や互換性が追加される予定です。実際のプロジェクトでBunを使用する前に、重要な依存関係の互換性を確認することをお勧めします。

おすすめの書籍

Bunサーバーの構築方法と実践例

Bunには高性能なHTTPサーバーが組み込まれており、Node.jsよりもはるかに少ないコードと高速なパフォーマンスでWebサーバーを構築できます。ここでは、Bunを使ったサーバー構築の基本から応用例までを紹介します。

最もシンプルなBunサーバー

Bunのサーバーは非常にシンプルに構築できます。以下は最小限のコードでHTTPサーバーを立ち上げる例です。

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

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

このコードをbun run server.tsで実行すれば、すぐにサーバーが起動します。Node.jsと比較して、外部ライブラリのインポートや複雑な設定が不要なことがわかります。

リクエストのルーティング

実際のアプリケーションでは、URLに基づいて異なるレスポンスを返す必要があります。Bunではリクエストオブジェクトを使用して簡単にルーティングを実装できます。

// router.ts
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);
    
    // ルートパスへのリクエスト
    if (url.pathname === "/") {
      return new Response("Welcome to Bun Server!");
    }
    
    // APIエンドポイント
    if (url.pathname === "/api/data") {
      const data = { message: "This is JSON data", time: new Date() };
      return new Response(JSON.stringify(data), {
        headers: { "Content-Type": "application/json" },
      });
    }
    
    // 静的ファイル
    if (url.pathname.startsWith("/public/")) {
      const filePath = url.pathname.replace("/public/", "");
      const file = Bun.file(`./public/${filePath}`);
      return new Response(file);
    }
    
    // 404 Not Found
    return new Response("Not Found", { status: 404 });
  },
});

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

RESTful APIの実装

Bunを使って完全なRESTful APIを構築する例を見てみましょう。

// api-server.ts
interface Todo {
  id: number;
  text: string;
  completed: boolean;
}

// インメモリデータストア
const todos: Todo[] = [
  { id: 1, text: "Learn Bun", completed: false },
  { id: 2, text: "Build a server", completed: true },
];

const server = Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);
    
    // CORS対応ヘッダー
    const headers = {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type",
    };
    
    // プリフライトリクエスト対応
    if (req.method === "OPTIONS") {
      return new Response(null, { headers });
    }
    
    // TODOsのエンドポイント
    if (url.pathname === "/api/todos") {
      // GETリクエスト - すべてのTODOを取得
      if (req.method === "GET") {
        return new Response(JSON.stringify(todos), {
          headers: { ...headers, "Content-Type": "application/json" },
        });
      }
      
      // POSTリクエスト - 新しいTODOを作成
      if (req.method === "POST") {
        const body = await req.json();
        const newTodo: Todo = {
          id: todos.length + 1,
          text: body.text,
          completed: false,
        };
        todos.push(newTodo);
        return new Response(JSON.stringify(newTodo), {
          headers: { ...headers, "Content-Type": "application/json" },
          status: 201,
        });
      }
    }
    
    // 特定のTODOのエンドポイント
    if (url.pathname.match(/^\/api\/todos\/\d+$/)) {
      const id = parseInt(url.pathname.split("/").pop() || "0");
      const todoIndex = todos.findIndex(todo => todo.id === id);
      
      if (todoIndex === -1) {
        return new Response(JSON.stringify({ error: "Todo not found" }), {
          headers: { ...headers, "Content-Type": "application/json" },
          status: 404,
        });
      }
      
      // GETリクエスト - 特定のTODOを取得
      if (req.method === "GET") {
        return new Response(JSON.stringify(todos[todoIndex]), {
          headers: { ...headers, "Content-Type": "application/json" },
        });
      }
      
      // PUTリクエスト - 特定のTODOを更新
      if (req.method === "PUT") {
        const body = await req.json();
        todos[todoIndex] = { ...todos[todoIndex], ...body };
        return new Response(JSON.stringify(todos[todoIndex]), {
          headers: { ...headers, "Content-Type": "application/json" },
        });
      }
      
      // DELETEリクエスト - 特定のTODOを削除
      if (req.method === "DELETE") {
        const deletedTodo = todos.splice(todoIndex, 1)[0];
        return new Response(JSON.stringify(deletedTodo), {
          headers: { ...headers, "Content-Type": "application/json" },
        });
      }
    }
    
    // 404 Not Found
    return new Response(JSON.stringify({ error: "Not Found" }), {
      headers: { ...headers, "Content-Type": "application/json" },
      status: 404,
    });
  },
});

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

既存のExpressアプリケーションの移行

Node.jsのExpressフレームワークを使用している場合、Bunでも同様に動作させることができます。

// express-app.ts
import express from 'express';

const app = express();
app.use(express.json());

app.get('/', (req, res) => {
  res.send('Express running on Bun!');
});

app.get('/api/hello', (req, res) => {
  res.json({ message: 'Hello from Express on Bun!' });
});

export default app;
// express-server.ts
import app from './express-app';

// Expressアプリをポート3000で起動
app.listen(3000, () => {
  console.log('Express server running on Bun at http://localhost:3000');
});

このExpressアプリケーションをbun run express-server.tsで実行することができます。既存のExpressアプリケーションをBunに移行する際は、このように最小限の変更で対応できる場合が多いです。

Bunサーバーのパフォーマンス最適化

Bunサーバーは最初から高性能ですが、さらに最適化するためのテクニックをいくつか紹介します。

  1. ストリーミングレスポンス:
const server = Bun.serve({
  port: 3000,
  fetch(req) {
    // 大きなファイルのストリーミング
    const file = Bun.file("large-data.csv");
    return new Response(file.stream());
  },
});
  1. サーバーサイドキャッシュ:
// シンプルなインメモリキャッシュ
const cache = new Map();

const server = Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);
    
    // キャッシュからレスポンスをチェック
    if (cache.has(url.pathname)) {
      return cache.get(url.pathname);
    }
    
    // 新しいレスポンスを生成
    const response = new Response(`Content for ${url.pathname}`);
    
    // キャッシュに保存(実際のアプリではTTLを設定する)
    cache.set(url.pathname, response.clone());
    
    return response;
  },
});

Bunサーバーは非常に柔軟で、小規模なAPIから大規模なWebアプリケーションまで、さまざまなユースケースに対応できます。内部的に最適化されたHTTPスタックにより、Node.jsベースのサーバーよりも高いパフォーマンスを発揮し、特に大量のリクエストを処理する必要があるアプリケーションで威力を発揮します。

おすすめの書籍

Bunと他のツールの互換性と移行のポイント

Bunは既存のJavaScriptエコシステムとの互換性を高めることを目指していますが、現時点では完全な互換性はまだ実現されていません。ここでは、Bunと他のJavaScriptツールとの互換性や、既存のプロジェクトを移行する際のポイントについて解説します。

Node.jsとの互換性

Bunは多くのNode.jsのAPIと互換性がありますが、一部の機能は異なる動作をするか、まだ実装されていない場合があります。

// Node.jsとBunの両方で動作するコード例
import fs from 'fs';
import path from 'path';

const filePath = path.join(process.cwd(), 'data.txt');
if (fs.existsSync(filePath)) {
  const content = fs.readFileSync(filePath, 'utf8');
  console.log(content);
}

ただし、次のような違いに注意が必要です:

  1. Bunのprocess.envはイミュータブル(変更不可)
  2. 一部のNode.js固有のAPIはサポートされていない
  3. CommonJSとESMの扱いに違いがある

移行前の互換性チェック

既存のNode.jsプロジェクトをBunに移行する前に、互換性をチェックするためのステップを紹介します:

  1. 依存関係のチェック:
# 依存関係のチェックツールを実行
bun run --bun node_modules/.bin/check-compat
  1. 徐々に移行する:

まずはテストやビルドプロセスなど、一部の機能だけをBunに移行することから始めると良いでしょう。例えば:

// package.json
{
  "scripts": {
    "start": "node index.js",
    "start:bun": "bun run index.js",
    "test": "jest",
    "test:bun": "bun test"
  }
}

主要フレームワークとの互換性

主要なJavaScriptフレームワークのBunとの互換性状況をまとめました:

フレームワーク 互換性 注意点
Express ✅ 良好 ほとんどの機能が動作
Next.js 🟡 部分的 開発サーバーはOK、一部のAPI Routes機能に制限あり
React ✅ 良好 JSX変換は内蔵されているため高速
Vue 🟡 部分的 基本機能は動作するが一部のプラグインに問題あり
Svelte ✅ 良好 コンパイルはBun内蔵のため高速

バンドラーとしてのBun

BunはWebpackやViteのような従来のバンドラーの代替としても使用できます。

# プロジェクトをバンドル
bun build ./index.ts --outdir ./dist

# 開発サーバー起動
bun --hot ./index.ts

バンドラーとしてのBunの特徴:

  • ネイティブなTypeScriptとJSXのサポート
  • デフォルトで最小限の設定で動作
  • 非常に高速なビルド時間
  • .envファイルの自動ロード

ただし、まだWebpackやViteほど多様なプラグインエコシステムはないため、特殊なビルド要件がある場合は注意が必要です。

テストランナーとしての互換性

Bunには組み込みのテストランナーが含まれており、Jest互換のAPIを提供します。

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

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

test("JSON parse/stringify", () => {
  const obj = { hello: "world" };
  expect(JSON.parse(JSON.stringify(obj))).toEqual(obj);
});

Jestユーザーにとって馴染みのある機能が多いですが、一部のJestの高度な機能(特定のモック機能など)はまだ完全にはサポートされていません。

本番環境への移行のポイント

Bunを本番環境で使用する際の注意点:

  1. 安定性の確認: Bunは急速に発展していますが、重要な本番環境では安定バージョンを使用
  2. モニタリング: 初期段階ではメモリ使用量やパフォーマンスを注意深くモニタリング
  3. フォールバック: 重要な機能では、Node.jsへのフォールバックを準備
  4. 依存関係の検証: すべての依存関係がBunと互換性があることを徹底的にテスト

Bunは非常に有望なツールですが、現時点ではNode.jsの完全な代替とはなっていません。用途や要件に応じて、適切に使い分けることが重要です。新しいプロジェクトや開発環境では積極的に採用し、本番環境への導入は慎重に検討することをお勧めします。

また、Bunはアクティブに開発が続けられているプロジェクトであり、日々改善されています。今後はさらに互換性が向上し、より多くのユースケースで安心して使用できるようになるでしょう。

おすすめの書籍

おすすめコンテンツ