bun-dev-server

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bun Development Server Setup

Bun开发服务器搭建

You are assisting with setting up a high-performance development server using Bun.serve with Hot Module Replacement (HMR) and React Fast Refresh.
本文将协助你使用Bun.serve搭建支持热模块替换(HMR)和React Fast Refresh的高性能开发服务器。

Workflow

操作流程

1. Determine Server Type

1. 确定服务器类型

Ask the user what type of development server they need:
  • React/Frontend App: SPA with React Fast Refresh
  • API Server: REST/GraphQL API with auto-reload
  • Full-Stack App: Frontend + API combined
  • Static Server: File server with live reload
询问用户所需的开发服务器类型:
  • React/前端应用:支持React Fast Refresh的单页应用(SPA)
  • API服务器:支持自动重载的REST/GraphQL API
  • 全栈应用:前端+API组合服务器
  • 静态服务器:支持实时重载的文件服务器

2. Check Prerequisites

2. 检查前置条件

bash
undefined
bash
undefined

Verify Bun installation

验证Bun安装情况

bun --version
bun --version

Check if project has package.json

检查项目是否存在package.json

ls -la package.json

If no package.json exists, suggest running `bun init` first.
ls -la package.json

若不存在package.json,建议先执行`bun init`初始化项目。

3. Install Dependencies

3. 安装依赖

For React Apps:
bash
bun add react react-dom
bun add -d @types/react @types/react-dom
For API with Hono (recommended):
bash
bun add hono
For Full-Stack:
bash
bun add react react-dom hono
bun add -d @types/react @types/react-dom
React应用所需依赖:
bash
bun add react react-dom
bun add -d @types/react @types/react-dom
使用Hono的API服务器(推荐):
bash
bun add hono
全栈应用所需依赖:
bash
bun add react react-dom hono
bun add -d @types/react @types/react-dom

4. Create Server Configuration

4. 创建服务器配置

React Development Server

React开发服务器

Create
server.ts
in the project root:
typescript
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" }));
    }
  }
}
Create
public/index.html
:
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>

  <!-- HMR Client -->
  <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>
Create
src/index.tsx
:
typescript
import { render } from 'react-dom';
import App from './App';

const root = document.getElementById('root');
render(<App />, root);
Create
src/App.tsx
:
typescript
export default function App() {
  return (
    <div>
      <h1>Welcome to Bun + React!</h1>
      <p>Edit src/App.tsx to see HMR in action</p>
    </div>
  );
}
在项目根目录创建
server.ts
typescript
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);

    // 用于HMR的WebSocket
    if (url.pathname === "/_hmr") {
      const upgraded = server.upgrade(request);
      if (upgraded) return undefined;
      return new Response("WebSocket升级失败", { status: 500 });
    }

    // 为SPA路由提供index.html
    if (url.pathname === "/" || !url.pathname.includes(".")) {
      return new Response(
        Bun.file("public/index.html"),
        { headers: { "Content-Type": "text/html" } }
      );
    }

    // 提供静态文件
    const filePath = `public${url.pathname}`;
    const file = Bun.file(filePath);

    if (await file.exists()) {
      return new Response(file);
    }

    return new Response("未找到资源", { status: 404 });
  },

  websocket: {
    open(ws) {
      clients.add(ws);
      console.log("HMR客户端已连接");
    },

    close(ws) {
      clients.delete(ws);
      console.log("HMR客户端已断开连接");
    },

    message(ws, message) {
      // 按需处理客户端消息
    },
  },
});

console.log(`🚀 开发服务器运行于 http://localhost:${server.port}`);

// 监听文件变化
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(`📝 文件已修改: ${event.path}`);

    // 通知所有连接的客户端重载
    for (const client of clients) {
      client.send(JSON.stringify({ type: "reload" }));
    }
  }
}
创建
public/index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bun + React应用</title>
</head>
<body>
  <div id="root"></div>
  <script type="module" src="/src/index.tsx"></script>

  <!-- HMR客户端 -->
  <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('🔄 正在重载...');
        window.location.reload();
      }
    });

    ws.addEventListener('close', () => {
      console.log('❌ HMR连接已丢失,正在重新连接...');
      setTimeout(() => window.location.reload(), 1000);
    });
  </script>
</body>
</html>
创建
src/index.tsx
typescript
import { render } from 'react-dom';
import App from './App';

const root = document.getElementById('root');
render(<App />, root);
创建
src/App.tsx
typescript
export default function App() {
  return (
    <div>
      <h1>欢迎使用Bun + React!</h1>
      <p>修改src/App.tsx即可体验HMR功能</p>
    </div>
  );
}

API Server with Hono

基于Hono的API服务器

Create
server.ts
:
typescript
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
typescript
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';

const app = new Hono();

// 中间件
app.use('*', cors());
app.use('*', logger());

// 路由
app.get('/', (c) => {
  return c.json({ message: '欢迎使用Bun API' });
});

app.get('/api/health', (c) => {
  return c.json({ status: 'ok', timestamp: Date.now() });
});

// 示例POST接口
app.post('/api/users', async (c) => {
  const body = await c.req.json();
  return c.json({ created: true, data: body }, 201);
});

// 启动服务器
const server = Bun.serve({
  port: process.env.PORT || 3000,
  fetch: app.fetch,
});

console.log(`🚀 API服务器运行于 http://localhost:${server.port}`);

Full-Stack Server

全栈服务器

Create
server.ts
:
typescript
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}`);
创建
server.ts
typescript
import { Hono } from 'hono';
import { serveStatic } from 'hono/bun';

const app = new Hono();

// API路由
const api = new Hono();

api.get('/health', (c) => c.json({ status: 'ok' }));
api.get('/users', (c) => c.json({ users: [] }));

app.route('/api', api);

// 提供静态文件
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(`🚀 全栈服务器运行于 http://localhost:${server.port}`);

5. Configure React Fast Refresh (Advanced)

5. 配置React Fast Refresh(进阶)

For true React Fast Refresh, create
hmr-runtime.ts
:
typescript
// 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();
  });
}
如需实现真正的React Fast Refresh,创建
hmr-runtime.ts
typescript
// React Fast Refresh运行时
let timeout: Timer | null = null;

export function refresh() {
  if (timeout) clearTimeout(timeout);

  timeout = setTimeout(() => {
    // 重新导入App组件
    import('./App.tsx?t=' + Date.now()).then((module) => {
      const { render } = require('react-dom');
      const root = document.getElementById('root');
      render(module.default(), root);
    });
  }, 100);
}

// 监听HMR事件
if (import.meta.hot) {
  import.meta.hot.accept(() => {
    refresh();
  });
}

6. Environment Configuration

6. 环境配置

Create
.env.development
:
bash
undefined
创建
.env.development
bash
undefined

Server

服务器配置

PORT=3000 NODE_ENV=development
PORT=3000 NODE_ENV=development

API

API配置

Features

功能开关

ENABLE_HMR=true

Create `.env.production`:

```bash
ENABLE_HMR=true

创建`.env.production`:

```bash

Server

服务器配置

PORT=8080 NODE_ENV=production
PORT=8080 NODE_ENV=production

API

API配置

Features

功能开关

ENABLE_HMR=false

Load environment in `server.ts`:

```typescript
// Environment is loaded automatically by Bun
const isDev = process.env.NODE_ENV === 'development';
const port = process.env.PORT || 3000;
ENABLE_HMR=false

在`server.ts`中加载环境变量:

```typescript
// Bun会自动加载环境变量
const isDev = process.env.NODE_ENV === 'development';
const port = process.env.PORT || 3000;

7. Update package.json Scripts

7. 更新package.json脚本

Add development scripts:
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"
  }
}
Script explanations:
  • dev
    : Run with hot reload (restarts on file changes)
  • dev:watch
    : Watch mode (faster, but doesn't reload on crash)
  • start
    : Production mode
  • build
    : Build frontend for production
添加开发相关脚本:
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. Configure TypeScript

8. 配置TypeScript

Update
tsconfig.json
:
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"]
}
更新
tsconfig.json
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. Create Project Structure

9. 创建项目结构

Generate complete project structure:
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
生成完整的项目结构:
project/
├── server.ts              # 开发服务器文件
├── src/
│   ├── index.tsx         # 应用入口
│   ├── App.tsx           # 主组件
│   ├── components/       # React组件目录
│   └── styles/           # CSS文件目录
├── public/
│   ├── index.html        # HTML模板
│   └── assets/           # 静态资源目录
├── .env.development
├── .env.production
├── package.json
├── tsconfig.json
└── README.md

10. Advanced: HTTPS for Local Development

10. 进阶:本地开发启用HTTPS

For HTTPS support (needed for some browser APIs):
typescript
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}`);
Generate certificates with:
bash
undefined
如需HTTPS支持(部分浏览器API需要):
typescript
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服务器运行于 https://localhost:${server.port}`);
使用以下命令生成证书:
bash
undefined

Install mkcert first: brew install mkcert

先安装mkcert: brew install mkcert

mkcert -install mkcert localhost 127.0.0.1 ::1
undefined
mkcert -install mkcert localhost 127.0.0.1 ::1
undefined

11. Proxy Configuration (for existing backends)

11. 代理配置(适用于已有后端服务)

If user needs to proxy API requests to another server:
typescript
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,
  });
});
若用户需要将API请求代理到其他服务器:
typescript
const app = new Hono();

// 将/api请求代理到后端
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,
  });
});

Testing the Setup

测试搭建结果

After creation, guide user to test:
bash
undefined
搭建完成后,引导用户进行测试:
bash
undefined

1. Start dev server

1. 启动开发服务器

bun run dev
bun run dev

2. Open browser

2. 打开浏览器

3. Make a change to src/App.tsx

3. 修改src/App.tsx文件

4. Verify HMR reloads the page

4. 验证HMR是否触发页面重载

5. Test API endpoints

5. 测试API接口

Troubleshooting

问题排查

HMR not working

HMR无法正常工作

typescript
// 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
typescript
undefined

Port already in use

检查WebSocket连接是否建立

打开浏览器控制台,查看是否有如下日志:

"HMR客户端已连接"

若未连接,验证以下内容:

1. 端口配置正确

2. 防火墙未拦截WebSocket

3. 服务器使用--hot参数启动

bash
undefined
undefined

Find process using port 3000

端口已被占用

lsof -ti:3000
bash
undefined

Kill the process

查找占用3000端口的进程

kill -9 $(lsof -ti:3000)
lsof -ti:3000

Or use a different port

终止该进程

PORT=3001 bun run dev
undefined
kill -9 $(lsof -ti:3000)

CORS issues

或使用其他端口

Add CORS headers to server:
typescript
app.use('*', cors({
  origin: 'http://localhost:3000',
  credentials: true,
}));
PORT=3001 bun run dev
undefined

Performance Tips

CORS问题

  1. Use --hot for development: Faster than --watch for most cases
  2. Minimize file watcher scope: Watch only src/ directory
  3. Use HTTP/2: Enable for faster parallel loading
  4. Cache static assets: Add Cache-Control headers
typescript
app.use('/assets/*', async (c, next) => {
  await next();
  c.header('Cache-Control', 'public, max-age=31536000');
});
在服务器中添加CORS头:
typescript
app.use('*', cors({
  origin: 'http://localhost:3000',
  credentials: true,
}));

Completion Checklist

性能优化建议

  • ✅ Development server created
  • ✅ HMR configured and tested
  • ✅ Environment variables set up
  • ✅ Package.json scripts added
  • ✅ Project structure organized
  • ✅ TypeScript configured
  • ✅ Browser successfully connects
  • ✅ File changes trigger reload
  1. 开发环境使用--hot参数:大多数情况下比--watch速度更快
  2. 缩小文件监听范围:仅监听src/目录
  3. 启用HTTP/2:提升并行加载速度
  4. 缓存静态资源:添加Cache-Control头
typescript
app.use('/assets/*', async (c, next) => {
  await next();
  c.header('Cache-Control', 'public, max-age=31536000');
});

Next Steps

完成检查清单

Suggest to the user:
  1. Add error boundaries for better error handling
  2. Set up ESLint and Prettier
  3. Configure path aliases in tsconfig.json
  4. Add development vs production builds
  5. Consider adding bun-test for testing
  • ✅ 已创建开发服务器
  • ✅ 已配置并测试HMR
  • ✅ 已设置环境变量
  • ✅ 已添加package.json脚本
  • ✅ 已整理项目结构
  • ✅ 已配置TypeScript
  • ✅ 浏览器已成功连接
  • ✅ 文件变化可触发重载

后续步骤

向用户建议:
  1. 添加错误边界以优化错误处理
  2. 配置ESLint和Prettier
  3. 在tsconfig.json中设置路径别名
  4. 区分开发与生产构建
  5. 考虑添加bun-test进行测试