bun-dev-server について
bun-dev-serverは、Bun.serveを使った開発サーバーの設定を案内するスキルです。React、API、フルスタックアプリ、静的ファイルに対応し、HMRとReact Fast Refreshの設定も扱います。
主な機能
- 01サーバー種別の選択
- 02React HMRの設定
- 03Hono APIサーバーの設定
- 04環境とスクリプトの設定
- 05HTTPSとAPIプロキシの設定
- 06一般的な問題のトラブルシューティング
活用シーン
- 01React開発サーバーの作成
- 02自動リロード対応APIサーバーの構築
- 03単一サーバーでのフロントエンドとAPIの統合
- 04既存バックエンドへのAPIリクエスト転送
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ daleseo/ bun-skills --skill bun-dev-serverインストール後、Claude Code で/bun-dev-serverを入力して読み込まれたか確認します。
同梱ファイルreferences/hmr-examples.md
詳細
Bun 開発サーバーのセットアップ
Bun.serve と Hot Module Replacement(HMR)、React Fast Refresh を使用した高パフォーマンスな開発サーバーのセットアップを支援します。
ワークフロー
1. サーバーの種類を決める
必要な開発サーバーの種類をユーザーに尋ねます。
- React/フロントエンドアプリ:React Fast Refresh を使う SPA
- API サーバー:自動リロード機能付きの REST/GraphQL API
- フルスタックアプリ:フロントエンドと API の組み合わせ
- 静的サーバー:ライブリロード機能付きのファイルサーバー
2. 前提条件を確認する
# Verify Bun installation
bun --version
# Check if project has package.json
ls -la package.json
package.json がない場合は、最初に bun init を実行するよう提案します。
3. 依存関係をインストールする
React アプリの場合:
bun add react react-dom
bun add -d @types/react @types/react-dom
Hono を使った API の場合(推奨):
bun add hono
フルスタックの場合:
bun add react react-dom hono
bun add -d @types/react @types/react-dom
4. サーバー設定を作成する
React 開発サーバー
プロジェクトのルートに server.ts を作成します。
import type { ServerWebSocket } from "bun";
const clients = new Set<ServerWebSocket<unknown>>();
const server = Bun.serve({
port: 3000,
async fetch(request, server) {
const url = new URL(request.url);
// WebSocket for HMR
if (url.pathname === "/_hmr") {
const upgraded = server.upgrade(request);
if (upgraded) return undefined;
return new Response("WebSocket upgrade failed", { status: 500 });
}
// Serve index.html for SPA routing
if (url.pathname === "/" || !url.pathname.includes(".")) {
return new Response(
Bun.file("public/index.html"),
{ headers: { "Content-Type": "text/html" } }
);
}
// Serve static files
const filePath = `public${url.pathname}`;
const file = Bun.file(filePath);
if (await file.exists()) {
return new Response(file);
}
return new Response("Not Found", { status: 404 });
},
websocket: {
open(ws) {
clients.add(ws);
console.log("HMR client connected");
},
close(ws) {
clients.delete(ws);
console.log("HMR client disconnected");
},
message(ws, message) {
// Handle client messages if needed
},
},
});
console.log(`🚀 Dev server running at http://localhost:${server.port}`);
// Watch for file changes
const watcher = Bun.file.watch(import.meta.dir + "/src", {
recursive: true,
});
for await (const event of watcher) {
if (event.kind === "change" && event.path.endsWith(".tsx")) {
console.log(`📝 File changed: ${event.path}`);
// Notify all connected clients to reload
for (const client of clients) {
client.send(JSON.stringify({ type: "reload" }));
}
}
}
public/index.html を作成します。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bun + React App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
<script>
const ws = new WebSocket('ws://localhost:3000/_hmr');
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'reload') {
console.log('🔄 Reloading...');
window.location.reload();
}
});
ws.addEventListener('close', () => {
console.log('❌ HMR connection lost. Reconnecting...');
setTimeout(() => window.location.reload(), 1000);
});
</script>
</body>
</html>
src/index.tsx を作成します。
import { render } from 'react-dom';
import App from './App';
const root = document.getElementById('root');
render(<App />, root);
src/App.tsx を作成します。
export default function App() {
return (
<div>
<h1>Welcome to Bun + React!</h1>
<p>Edit src/App.tsx to see HMR in action</p>
</div>
);
}
Hono を使った API サーバー
server.ts を作成します。
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
const app = new Hono();
// Middleware
app.use('*', cors());
app.use('*', logger());
// Routes
app.get('/', (c) => {
return c.json({ message: 'Welcome to Bun API' });
});
app.get('/api/health', (c) => {
return c.json({ status: 'ok', timestamp: Date.now() });
});
// Example POST endpoint
app.post('/api/users', async (c) => {
const body = await c.req.json();
return c.json({ created: true, data: body }, 201);
});
// Start server
const server = Bun.serve({
port: process.env.PORT || 3000,
fetch: app.fetch,
});
console.log(`🚀 API server running at http://localhost:${server.port}`);
フルスタックサーバー
server.ts を作成します。
import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';
const app = new Hono();
// API routes
const api = new Hono();
api.get('/health', (c) => c.json({ status: 'ok' }));
api.get('/users', (c) => c.json({ users: [] }));
app.route('/api', api);
// Serve static files
app.use('/*', serveStatic({ root: './public' }));
// SPA fallback
app.get('*', (c) => c.html(Bun.file('public/index.html')));
const server = Bun.serve({
port: 3000,
fetch: app.fetch,
});
console.log(`🚀 Full-stack server at http://localhost:${server.port}`);
5. React Fast Refresh を設定する(上級者向け)
React Fast Refresh を適切に機能させるには、hmr-runtime.ts を作成します。
// React Fast Refresh runtime
let timeout: Timer | null = null;
export function refresh() {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
// Re-import the App component
import('./App.tsx?t=' + Date.now()).then((module) => {
const { render } = require('react-dom');
const root = document.getElementById('root');
render(module.default(), root);
});
}, 100);
}
// Listen for HMR events
if (import.meta.hot) {
import.meta.hot.accept(() => {
refresh();
});
}
6. 環境設定
.env.development を作成します。
# Server
PORT=3000
NODE_ENV=development
# API
API_URL=http://localhost:3000/api
# Features
ENABLE_HMR=true
.env.production を作成します。
# Server
PORT=8080
NODE_ENV=production
# API
API_URL=https://api.example.com
# Features
ENABLE_HMR=false
server.ts で環境変数を読み込みます。
// Environment is loaded automatically by Bun
const isDev = process.env.NODE_ENV === 'development';
const port = process.env.PORT || 3000;
7. package.json のスクリプトを更新する
開発用スクリプトを追加します。
{
"scripts": {
"dev": "bun run --hot server.ts",
"dev:watch": "bun run --watch server.ts",
"start": "NODE_ENV=production bun run server.ts",
"build": "bun build src/index.tsx --outdir=dist --minify",
"clean": "rm -rf dist"
}
}
スクリプトの説明:
dev:ホットリロードで実行します(ファイルの変更時に再起動します)dev:watch:ウォッチモードで実行します(より高速ですが、クラッシュ時に再読み込みしません)start:本番モードbuild:本番用にフロントエンドをビルドします
8. TypeScript を設定する
tsconfig.json を更新します。
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"types": ["bun-types"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"noEmit": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*", "server.ts"]
}
9. プロジェクト構成を作成する
プロジェクト構成全体を生成します。
project/
├── server.ts # Development server
├── src/
│ ├── index.tsx # App entry point
│ ├── App.tsx # Main component
│ ├── components/ # React components
│ └── styles/ # CSS files
├── public/
│ ├── index.html # HTML template
│ └── assets/ # Static assets
├── .env.development
├── .env.production
├── package.json
├── tsconfig.json
└── README.md
10. 上級者向け:ローカル開発用 HTTPS
HTTPS を有効にする場合(一部のブラウザー API で必要です):
import { readFileSync } from 'fs';
const server = Bun.serve({
port: 3000,
tls: {
cert: readFileSync('./localhost.pem'),
key: readFileSync('./localhost-key.pem'),
},
fetch: app.fetch,
});
console.log(`🔒 HTTPS server at https://localhost:${server.port}`);
次のコマンドで証明書を生成します。
# Install mkcert first: brew install mkcert
mkcert -install
mkcert localhost 127.0.0.1 ::1
11. プロキシ設定(既存のバックエンド向け)
API リクエストを別のサーバーにプロキシする必要がある場合:
const app = new Hono();
// Proxy /api requests to backend
app.all('/api/*', async (c) => {
const url = new URL(c.req.url);
const backendUrl = `http://localhost:8080${url.pathname}${url.search}`;
const response = await fetch(backendUrl, {
method: c.req.method,
headers: c.req.raw.headers,
body: c.req.method !== 'GET' ? await c.req.raw.text() : undefined,
});
return new Response(response.body, {
status: response.status,
headers: response.headers,
});
});
セットアップのテスト
作成後、次の手順でテストするようユーザーを案内します。
# 1. Start dev server
bun run dev
# 2. Open browser
open http://localhost:3000
# 3. Make a change to src/App.tsx
# 4. Verify HMR reloads the page
# 5. Test API endpoints
curl http://localhost:3000/api/health
トラブルシューティング
HMR が機能しない
// Check if WebSocket connection is established
// Open browser console and look for:
// "HMR client connected"
// If not, verify:
// 1. Port is correct
// 2. No firewall blocking WebSocket
// 3. Server is running with --hot flag
ポートがすでに使用されている
# Find process using port 3000
lsof -ti:3000
# Kill the process
kill -9 $(lsof -ti:3000)
# Or use a different port
PORT=3001 bun run dev
CORS の問題
サーバーに CORS ヘッダーを追加します。
app.use('*', cors({
origin: 'http://localhost:3000',
credentials: true,
}));
パフォーマンスのヒント
- 開発時には --hot を使用する:ほとんどの場合、--watch より高速です
- ファイルウォッチャーの対象範囲を最小限にする:src/ ディレクトリのみを監視します
- HTTP/2 を使用する:有効にすると、並列読み込みが高速になります
- 静的アセットをキャッシュする:Cache-Control ヘッダーを追加します
app.use('/assets/*', async (c, next) => {
await next();
c.header('Cache-Control', 'public, max-age=31536000');
});
完了チェックリスト
- ✅ 開発サーバーを作成済み
- ✅ HMR を設定し、テスト済み
- ✅ 環境変数を設定済み
- ✅ package.json にスクリプトを追加済み
- ✅ プロジェクト構成を整理済み
- ✅ TypeScript を設定済み
- ✅ ブラウザーが正常に接続
- ✅ ファイルの変更時に再読み込みが発生
次のステップ
ユーザーに次の項目を提案します。
- エラー処理を改善するためにエラーバウンダリーを追加する
- ESLint と Prettier をセットアップする
- tsconfig.json でパスエイリアスを設定する
- 開発用と本番用のビルドを追加する
- テスト用に bun-test の追加を検討する