Tasuke HubLearn · Solve · Grow
#JavaScript

Node.jsからBunへの完全移行ガイド - 実践的な手順とパフォーマンス向上の全て【2025年版】

Node.jsからBunへの移行を成功させるための完全ガイド。実際のプロジェクトでの移行手順、パフォーマンステスト、トラブルシューティングまで実践的なノウハウを詳しく解説します。

時計のアイコン11 August, 2025

Node.jsの代替として注目を集めているBun。2025年、Bun 1.1でWindows対応を果たし、さらにBun 1.2.19ではパフォーマンスが大幅に向上しました。本記事では、実際のプロジェクトでNode.jsからBunへ移行する具体的な手順と実践的なノウハウを詳しく解説します。

TH

Tasuke Hub管理人

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

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

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

なぜ今Bunなのか?

Bunは単なるJavaScriptランタイムではありません。パッケージマネージャー、バンドラー、テストランナー、タスクランナーが統合された開発体験を提供します。

Bunの主要な優位性

  • 統合開発体験: npm、webpack、jest、nodeを1つのツールで代替
  • 高速パフォーマンス: JavaScriptCoreエンジンによる高速実行
  • クロスプラットフォーム: Windows、macOS、Linux対応(2025年時点)
  • 互換性重視: 既存のNode.jsプロジェクトとnpmライブラリが動作
ベストマッチ

最短で課題解決する一冊

この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。

Bunのインストール

macOS・Linux

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

Windows(PowerShell)

irm bun.sh/install.ps1 | iex

インストール確認

bun --version
# 出力例: 1.2.19

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

実際のプロジェクトで移行体験

Express.jsを使った実際のAPIサーバーをNode.jsからBunへ移行してみましょう。

1. 既存のNode.jsプロジェクト

// server.js - Node.js版
import express from 'express';
import cors from 'cors';
import { readFile } from 'fs/promises';

const app = express();
const PORT = process.env.PORT || 3000;

app.use(cors());
app.use(express.json());

// ファイル読み込みのパフォーマンステスト用エンドポイント
app.get('/api/data', async (req, res) => {
  try {
    const data = await readFile('./data/sample.json', 'utf8');
    const parsed = JSON.parse(data);
    
    // 重い処理をシミュレート(配列の変換処理)
    const processed = parsed.items.map(item => ({
      ...item,
      processed: true,
      timestamp: new Date().toISOString()
    }));
    
    res.json({ 
      success: true, 
      data: processed,
      processedAt: Date.now()
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(PORT, () => {
  console.log(`Node.js server running on port ${PORT}`);
});

2. Bunに最適化されたバージョン

// server.js - Bun版
import { serve } from 'bun';

const server = serve({
  port: process.env.PORT || 3000,
  
  async fetch(req) {
    const url = new URL(req.url);
    
    // CORS対応
    const corsHeaders = {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    };
    
    if (req.method === 'OPTIONS') {
      return new Response(null, { headers: corsHeaders });
    }
    
    // APIルーティング
    if (url.pathname === '/api/data' && req.method === 'GET') {
      try {
        // Bunの高速ファイル読み込みAPI使用
        const file = Bun.file('./data/sample.json');
        const data = await file.json();
        
        // データ処理(Bunの最適化されたJavaScriptCoreで実行)
        const processed = data.items.map(item => ({
          ...item,
          processed: true,
          timestamp: new Date().toISOString()
        }));
        
        return Response.json({
          success: true,
          data: processed,
          processedAt: Date.now(),
          runtime: 'Bun'
        }, { headers: corsHeaders });
        
      } catch (error) {
        return Response.json(
          { error: error.message }, 
          { status: 500, headers: corsHeaders }
        );
      }
    }
    
    // 404処理
    return new Response('Not Found', { status: 404, headers: corsHeaders });
  },
});

console.log(`Bun server running on port ${server.port}`);

3. パッケージ管理の移行

# Node.js プロジェクトの依存関係をBunで管理
rm -rf node_modules package-lock.json

# Bunで依存関係をインストール
bun install

# 新しい依存関係の追加
bun add express cors
bun add -d @types/node typescript

# 開発用依存関係の追加
bun add -d nodemon concurrently

4. スクリプトの最適化

{
  "name": "node-to-bun-migration",
  "version": "1.0.0",
  "scripts": {
    "dev": "bun --watch server.js",
    "start": "bun server.js",
    "build": "bun build ./src/index.ts --outdir ./dist --target node",
    "test": "bun test",
    "type-check": "bunx tsc --noEmit"
  },
  "dependencies": {
    "express": "^4.19.2",
    "cors": "^2.8.5"
  },
  "devDependencies": {
    "@types/node": "^20.14.12",
    "typescript": "^5.5.4"
  }
}

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

パフォーマンス比較テスト

実際のパフォーマンステストを行いました。

ベンチマークスクリプト

// benchmark.js - パフォーマンステスト用スクリプト
import { performance } from 'perf_hooks';

// ファイル読み込み速度テスト
async function fileReadTest() {
  const iterations = 1000;
  const start = performance.now();
  
  for (let i = 0; i < iterations; i++) {
    // Bunの場合: Bun.file() API使用
    // Node.jsの場合: fs.readFile() 使用
    if (typeof Bun !== 'undefined') {
      const file = Bun.file('./data/sample.json');
      await file.json();
    } else {
      const { readFile } = await import('fs/promises');
      const data = await readFile('./data/sample.json', 'utf8');
      JSON.parse(data);
    }
  }
  
  const end = performance.now();
  return end - start;
}

// HTTP サーバー起動速度テスト
async function serverStartupTest() {
  const start = performance.now();
  
  if (typeof Bun !== 'undefined') {
    // Bunの場合
    const server = Bun.serve({
      port: 0,
      fetch: () => new Response('OK')
    });
    server.stop();
  } else {
    // Node.jsの場合
    const express = (await import('express')).default;
    const app = express();
    const server = app.listen(0);
    server.close();
  }
  
  const end = performance.now();
  return end - start;
}

// ベンチマーク実行
async function runBenchmarks() {
  const runtime = typeof Bun !== 'undefined' ? 'Bun' : 'Node.js';
  console.log(`${runtime} パフォーマンステスト結果:`);
  
  const fileTime = await fileReadTest();
  console.log(`ファイル読み込み (1000回): ${fileTime.toFixed(2)}ms`);
  
  const startupTime = await serverStartupTest();
  console.log(`サーバー起動時間: ${startupTime.toFixed(2)}ms`);
}

runBenchmarks().catch(console.error);

テスト実行

# Node.jsでの実行
node benchmark.js

# Bunでの実行  
bun benchmark.js

結果例(筆者の環境)

Node.js パフォーマンステスト結果:
ファイル読み込み (1000回): 1247.83ms
サーバー起動時間: 23.45ms

Bun パフォーマンステスト結果:
ファイル読み込み (1000回): 892.31ms
サーバー起動時間: 8.72ms

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

実用的な移行チェックリスト

✅ 移行前の確認事項

// migration-check.js - 移行前の互換性チェック
import { execSync } from 'child_process';
import { readFileSync } from 'fs';

function checkProjectCompatibility() {
  console.log('🔍 Bun互換性チェックを開始...\n');
  
  // package.json の確認
  const packageJson = JSON.parse(readFileSync('./package.json', 'utf8'));
  
  // 問題のある依存関係をチェック
  const problematicPackages = [
    'node-gyp',      // ネイティブモジュール
    'bcrypt',        // バイナリ依存
    'sharp',         // 画像処理
    'sqlite3'        // データベース系
  ];
  
  const dependencies = {
    ...packageJson.dependencies || {},
    ...packageJson.devDependencies || {}
  };
  
  const issues = problematicPackages.filter(pkg => 
    Object.keys(dependencies).includes(pkg)
  );
  
  if (issues.length > 0) {
    console.log('⚠️  注意が必要なパッケージが見つかりました:');
    issues.forEach(pkg => {
      console.log(`   - ${pkg}: 互換性テストが推奨されます`);
    });
  } else {
    console.log('✅ 主要な互換性問題は見つかりませんでした');
  }
  
  // Node.jsのバージョン固有API使用をチェック
  try {
    const srcFiles = execSync('find ./src -name "*.js" -o -name "*.ts"', 
      { encoding: 'utf8' }).split('\n').filter(Boolean);
      
    const nodeSpecificPatterns = [
      'process.version',
      'require.resolve',
      '__dirname',
      '__filename'
    ];
    
    srcFiles.forEach(file => {
      const content = readFileSync(file, 'utf8');
      nodeSpecificPatterns.forEach(pattern => {
        if (content.includes(pattern)) {
          console.log(`⚠️  ${file}: ${pattern} の使用を確認してください`);
        }
      });
    });
  } catch (error) {
    console.log('ℹ️  ソースファイルのスキャンをスキップしました');
  }
}

checkProjectCompatibility();

🛠️ 段階的移行手順

#!/bin/bash
# migrate-to-bun.sh - 自動化された移行スクリプト

echo "🚀 Node.js → Bun 移行スクリプト"

# Step 1: バックアップ作成
echo "📦 プロジェクトのバックアップを作成..."
cp -r . ../$(basename "$PWD")-backup-$(date +%Y%m%d)

# Step 2: 既存の依存関係クリア
echo "🧹 既存の依存関係をクリア..."
rm -rf node_modules
rm -f package-lock.json yarn.lock

# Step 3: Bunでの再インストール
echo "📥 Bunで依存関係を再インストール..."
bun install

# Step 4: テスト実行
echo "🧪 テストを実行..."
if bun test; then
    echo "✅ テストが成功しました"
else
    echo "❌ テストでエラーが発生しました。修正が必要です。"
    exit 1
fi

# Step 5: 開発サーバー起動テスト
echo "🌐 開発サーバーのテスト..."
timeout 10s bun run dev &
sleep 5

if curl -f http://localhost:3000 > /dev/null 2>&1; then
    echo "✅ サーバーが正常に起動しました"
    pkill -f "bun run dev"
else
    echo "⚠️  サーバー起動に問題がある可能性があります"
    pkill -f "bun run dev"
fi

echo "🎉 移行プロセスが完了しました!"

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

トラブルシューティング

よくある問題と解決策

1. ESMインポートエラー

// ❌ 問題のあるコード
const express = require('express');

// ✅ 修正版 - ESMを使用
import express from 'express';

// CommonJS混在時の対応
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const legacyModule = require('legacy-commonjs-module');

2. 環境変数の処理

// ❌ Node.js特有の処理
process.env.NODE_ENV = 'production';

// ✅ Bun対応版
import { env } from 'process';
env.NODE_ENV = 'production';

// または process.env を直接使用(Bunでも動作)
process.env.NODE_ENV = 'production';

3. ファイルパス処理

// ❌ Node.js特有の __dirname
const configPath = path.join(__dirname, 'config.json');

// ✅ ESM対応版
import { fileURLToPath } from 'url';
import path from 'path';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const configPath = path.join(__dirname, 'config.json');

// 🚀 Bun推奨版
const configPath = new URL('./config.json', import.meta.url).pathname;

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

プロダクション対応

Docker化

# Dockerfile - Bun対応版
FROM oven/bun:1.2.19-slim

WORKDIR /app

# 依存関係のインストール(キャッシュ効率化)
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production

# アプリケーションコード
COPY . .

# 本番環境用の最適化
ENV NODE_ENV=production
ENV BUN_ENV=production

# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

EXPOSE 3000

# Bunでアプリケーション実行
CMD ["bun", "start"]

CI/CD パイプライン

# .github/workflows/bun-ci.yml
name: Bun CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Setup Bun
      uses: oven-sh/setup-bun@v2
      with:
        bun-version: '1.2.19'
    
    - name: Install dependencies
      run: bun install --frozen-lockfile
    
    - name: Type check
      run: bunx tsc --noEmit
    
    - name: Run tests
      run: bun test
    
    - name: Build application
      run: bun run build
    
    - name: Performance benchmark
      run: bun run benchmark

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

まとめ

Bunへの移行は、適切な準備と段階的なアプローチによって実現できます。

移行のメリット

  • 開発体験の向上: 統合されたツールチェーン
  • パフォーマンス向上: 起動時間とファイルI/Oの高速化
  • モダンな開発環境: TypeScript、ESMのネイティブサポート

移行時の注意点

  • 段階的な移行: 一度にすべてを変更しない
  • 十分なテスト: 既存の機能が正常に動作することを確認
  • チーム全体での合意: 新しいツールセットへの学習コスト

2025年現在、BunはNode.jsの実用的な代替選択肢として十分成熟しています。本記事の手順を参考に、ぜひあなたのプロジェクトでもBunを試してみてください。

さらに理解を深める参考書

関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。

この記事をシェア

続けて読みたい記事

編集部がピックアップした関連記事で学びを広げましょう。

#Go

GoとGinによるハイパフォーマンスAPIサーバー構築ガイド【2025年版】

2025/9/19
#API

API レスポンス遅延完全解決ガイド【2025年実務パフォーマンス最適化決定版】

2025/8/17
#GraphQL

GraphQL N+1問題完全解決ガイド【2025年実務パフォーマンス最適化決定版】

2025/8/19
#Rust

RustとWASMによるハイパフォーマンスサーバーレス関数開発ガイド【2025年版】

2025/9/19
#Node.js

Node.js 2025年7月重大脆弱性の完全対応ガイド【CVE-2025-27210/27209実践的解決策】

2025/8/17
#Security

Secrets/環境変数の実践ガイド【2025年版】:Next.js/Node/CI/CDの安全な管理

2025/9/13