Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← Web 开发 / Web Development

Node.js 与框架 / Node.js & Frameworks

1. Node.js 与后端框架学习路线 / Node.js and Backend Frameworks Learning Path

2. Node.js 运行时内部原理 / Node.js Runtime Internals

3. Node.js 异步模式与错误处理 / Async Patterns and Error Handling in Node.js

4. Express.js 深度剖析 / Express.js Deep Dive

5. Express 高级模式与生产实践 / Express Advanced Patterns and Production Practices

6. 现代 Node.js 框架对比 / Modern Node.js Framework Comparison

7. RESTful API 设计原则与实践 / RESTful API Design Principles and Practice

8. Node.js 环境配置与 12-Factor App / Environment Configuration and 12-Factor App

本页目录

现代 Node.js 框架对比 / Modern Node.js Framework Comparison ​

📅 创建时间:2026-07-28 🏷️ 标签:#Fastify #Hono #NestJS #FrameworkComparison #Nitro #Elysia 📚 前置知识:[[03-express-deep-dive]]


📋 本章目标 ​

  • 深入理解 Fastify 插件封装、Schema 驱动序列化与 Hook 生命周期
  • 掌握 Hono 的超轻量 Edge 优先架构与 RPC 类型安全调用模式
  • 理解 NestJS 依赖注入容器、装饰器驱动编程与管道模型
  • 了解 Nitro 的跨平台部署抽象与 unjs 工具链生态
  • 评估 Bun + Elysia 的原生性能优势与生态兼容现状
  • 建立系统化的框架选型决策能力,能根据场景做出合理技术选择

第1部分:Fastify 深度解析 ​

1.1 Fastify 的设计哲学 ​

┌─────────────────────────────────────────────────────────────┐
│                    Fastify 核心设计哲学                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Express 的教训:                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ • 中间件过于灵活 → 性能无法优化                      │   │
│  │ • 无 Schema 约束 → 运行时类型不安全                  │   │
│  │ • 缺少封装机制 → 大型项目难以治理                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                            ↓                                │
│  Fastify 的回应:                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ • Schema 优先 → 编译时 JSON Schema 验证 + 序列化     │   │
│  │ • 插件封装 → 隔离作用域,子插件不影响父插件           │   │
│  │ • Hook 生命周期 → 精细化控制请求处理各阶段           │   │
│  │ • 装饰器模式 → 类型安全地扩展实例能力                │   │
│  │ • 性能即功能 → 框架层面不牺牲吞吐量                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  核心理念:约定优于配置,但允许精准控制                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

1.2 插件系统与封装 ​

Fastify 的插件机制是其架构中最核心的设计。与 Express 的全局中间件不同,Fastify 的每个插件运行在独立的"封装上下文"中。

typescript
// fastify-plugin vs register:关键区别
import fastify from 'fastify'
import fp from 'fastify-plugin'

const app = fastify()

// ── 方式1:普通 register(封装隔离) ──
app.register(async function userRoutes(instance, opts) {
  // 此 instance 是 app 的子作用域
  // 这里注册的路由、装饰器、Hook 不会泄漏到外部

  instance.decorate('getUser', async (id: string) => {
    return { id, name: 'Alice' }
  })

  instance.get('/users/:id', async (request, reply) => {
    const user = await instance.getUser(request.params.id)
    return user
  })
})

// 外部无法访问 getUser —— 它在 userRoutes 作用域内
// app.getUser === undefined  ✅ 封装隔离

// ── 方式2:fastify-plugin(共享作用域) ──
app.register(fp(async function sharedPlugin(instance, opts) {
  // 此 instance 就是 app 本身(或父作用域)
  // 装饰器会挂载到父作用域,全局可见

  instance.decorate('db', createDbPool(opts.dbUrl))
}))

// app.db 可用  ✅ 全局共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
┌─────────────────────────────────────────────────────────────┐
│                 register vs fastify-plugin 作用域             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  app                                                        │
│  ├── register(pluginA)          ← 子作用域                  │
│  │   ├── 装饰器仅在此可见                                  │
│  │   ├── Hook 仅影响此子树                                  │
│  │   └── register(pluginA1)     ← 更深子作用域              │
│  │                                                          │
│  ├── register(fp(pluginB))      ← 不创建子作用域            │
│  │   ├── 装饰器在 app 层可见                                │
│  │   └── 适合:数据库连接、认证中间件等全局组件             │
│  │                                                          │
│  └── register(pluginC)          ← 另一个独立子作用域        │
│                                                             │
│  设计原则:                                                  │
│  • 业务路由 → register(封装隔离,避免命名冲突)            │
│  • 基础设施 → fp + register(全局共享,避免重复初始化)     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

1.3 Schema 驱动的序列化——为什么比 JSON.stringify 快 2-3 倍 ​

┌─────────────────────────────────────────────────────────────┐
│            Schema 驱动序列化 vs JSON.stringify               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  JSON.stringify 的工作方式:                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 1. 运行时遍历对象的所有属性                           │   │
│  │ 2. 检查每个属性的类型(typeof 判断)                  │   │
│  │ 3. 逐字段拼接 JSON 字符串                             │   │
│  │ 4. 每次请求都重复以上步骤                              │   │
│  │                                                      │   │
│  │ 问题:无类型信息,每次都要重新"发现"对象结构           │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Fastify 的 Schema 驱动序列化:                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 1. 应用启动时,根据 JSON Schema 预编译序列化函数      │   │
│  │ 2. 生成专门的 JavaScript 代码(类似预编译模板)       │   │
│  │ 3. 运行时:直接执行预编译函数 → 极快                  │   │
│  │                                                      │   │
│  │ 以响应 { "id": 1, "name": "Alice" } 为例:           │   │
│  │ JSON.stringify:  遍历 → typeof 检查 → 拼接(~2000ns) │   │
│  │ fast-json-stringify: 直接拼接(~600ns)               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  原理:JSON Schema 提供了完整的类型信息,                     │
│  使框架可以在启动时生成"已经知道对象长什么样"的序列化代码。  │
│  运行时不需要任何类型判断。                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
typescript
import fastify from 'fastify'

const app = fastify()

// 定义 Route Schema(同时用于验证和序列化)
app.get('/users/:id', {
  schema: {
    params: {
      type: 'object',
      properties: {
        id: { type: 'integer' }
      },
      required: ['id']
    },
    response: {
      200: {
        type: 'object',
        properties: {
          id: { type: 'integer' },
          name: { type: 'string' },
          email: { type: 'string', format: 'email' }
        }
      }
    }
  }
}, async (request, reply) => {
  // params 自动验证并转换类型
  // request.params.id 已经是 number 类型,不是 string
  const user = await db.findUser(request.params.id)

  // 返回时自动:
  // 1. 按 response schema 过滤字段(移除多余属性)
  // 2. 用预编译的序列化器序列化(比 JSON.stringify 快 2-3 倍)
  return user
})

// Schema 还自动生成 Swagger / OpenAPI 文档
// 只需注册 @fastify/swagger 即可获得交互式 API 文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

额外收益:Schema 定义同时驱动了三件事——输入验证、输出序列化、OpenAPI 文档生成。一份定义,三处受益。

1.4 Hook 生命周期 ​

┌─────────────────────────────────────────────────────────────┐
│                Fastify 请求生命周期 Hook 链                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  HTTP 请求到达                                               │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ onRequest       ← 请求到达,body 尚未解析            │   │
│  │                 适用:限流、IP 黑白名单              │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ preParsing      ← 原始 body 流尚未解析               │   │
│  │                 适用:原始 body 操作(签名验证)     │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ preValidation   ← body 已解析,Schema 验证前         │   │
│  │                 适用:认证、授权检查                 │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ preHandler      ← Schema 验证已通过                  │   │
│  │                 适用:请求级日志、额外权限检查       │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ preSerialization ← handler 返回后,序列化前          │   │
│  │                 适用:修改响应体、添加元数据         │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ onSend          ← 序列化后,发送前                   │   │
│  │                 适用:添加响应头、ETag/缓存头        │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ onResponse      ← 响应已发送给客户端                 │   │
│  │                 适用:请求完成日志、Metrics 打点     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ⚡ 每个 Hook 都可以是 async 函数,支持 await               │
│  ⚡ 多个同类型 Hook 按注册顺序串行执行                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
typescript
// Hook 实战:认证 + 响应日志
import fastify from 'fastify'

const app = fastify({ logger: true })

// 全局 preValidation Hook —— JWT 认证
app.addHook('preValidation', async (request, reply) => {
  // 跳过公开路由
  if (request.url.startsWith('/public')) return

  const token = request.headers.authorization?.replace('Bearer ', '')
  if (!token) {
    return reply.status(401).send({ error: 'Missing token' })
  }

  try {
    request.user = await jwtVerify(token, process.env.JWT_SECRET!)
  } catch {
    return reply.status(401).send({ error: 'Invalid token' })
  }
})

// 全局 onResponse Hook —— 请求耗时记录
app.addHook('onResponse', (request, reply, done) => {
  request.log.info({
    method: request.method,
    url: request.url,
    statusCode: reply.statusCode,
    responseTime: reply.elapsedTime, // Fastify 内置
  }, 'Request completed')
  done()
})

// 路由级 Hook —— 仅影响当前作用域
app.register(async function adminRoutes(instance) {
  instance.addHook('preHandler', async (request, reply) => {
    if (request.user?.role !== 'admin') {
      return reply.status(403).send({ error: 'Admin only' })
    }
  })

  instance.get('/admin/dashboard', async () => ({ stats: '...' }))
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

1.5 装饰器模式(Decorate) ​

typescript
// Fastify 的装饰器允许类型安全地扩展实例属性
// 与 Express 往 req 上挂任意属性不同,Fastify 的装饰器有类型推导

import fastify, { FastifyInstance, FastifyRequest } from 'fastify'

// Step 1: 通过 declare merging 扩展类型
declare module 'fastify' {
  interface FastifyInstance {
    db: DatabasePool
    cache: CacheClient
  }
  interface FastifyRequest {
    user: { id: number; role: string }
    requestId: string
  }
}

const app = fastify()

// Step 2: 在插件中装饰实例
app.register(fp(async function infraPlugin(instance) {
  instance.decorate('db', createDbPool())
  instance.decorate('cache', createCacheClient())
}))

app.register(fp(async function authPlugin(instance) {
  instance.decorateRequest('user', null) // 初始化占位
  instance.decorateRequest('requestId', '')

  instance.addHook('preValidation', async (request) => {
    request.requestId = request.headers['x-request-id'] as string
      ?? crypto.randomUUID()
    // request.user 由认证 Hook 设置
  })
}))

// Step 3: 在 handler 中使用(完整的类型推导)
app.get('/profile', async (request) => {
  // request.user.id     ← 类型安全
  // request.requestId   ← 类型安全
  // app.db.query(...)   ← 类型安全
  const profile = await app.db.query(
    'SELECT * FROM profiles WHERE user_id = $1',
    [request.user.id]
  )
  // app.cache 也可用
  return profile
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

1.6 Express 兼容层 ​

typescript
// @fastify/express 允许在 Fastify 中复用 Express 中间件
// 原理:将 Express 的 (req, res, next) 签名适配到 Fastify 的 Hook 模型

import fastify from 'fastify'
import fastifyExpress from '@fastify/express'

const app = fastify()
await app.register(fastifyExpress)

// 使用 Express 中间件
app.use(require('cors')())
app.use(require('helmet')())
app.use(require('morgan')('combined'))

// 但 Fastify 原生方案总是更好:
// @fastify/cors  代替 cors
// @fastify/helmet 代替 helmet

// 兼容层的限制:
// • Express 中间件不能使用 Fastify 的 async/await 错误处理
// • 性能比原生 Fastify 插件差(内部做了适配包装)
// • 类型推导弱于 Fastify 原生装饰器

// 建议:仅作为迁移桥梁使用,新代码全部用 Fastify 原生方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

第2部分:Hono 深度解析 ​

2.1 超轻量设计与跨运行时架构 ​

┌─────────────────────────────────────────────────────────────┐
│                    Hono 跨运行时架构                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  核心库体积:14KB (min+gzip)                                 │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Hono Core                         │   │
│  │         路由匹配 + 中间件引擎 + 上下文管理            │   │
│  └──────────┬──────────┬──────────┬──────────┬─────────┘   │
│             ↓          ↓          ↓          ↓              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  Node.js │  │  Deno    │  │   Bun    │  │Cloudflare│   │
│  │  Adapter │  │  Adapter │  │  Adapter │  │  Workers │   │
│  └──────────┘  └──────────┘  └──────────┘  │  Adapter │   │
│                                              └──────────┘   │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              Web Standard API 抽象层                 │   │
│  │  Request / Response / Headers / URL / FormData      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  关键设计决策:                                              │
│  • 不依赖 Node.js 特有 API(fs, net, http)                 │
│  • 完全基于 Web Standard API(Request / Response)          │
│  • 每个运行时提供最小适配层                                  │
│  • 同一套代码可以部署到任何环境                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typescript
// Hono 的"同一套代码,任意部署"
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Hello from Hono!'))
app.get('/api/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: `User ${id}` })
})

// Node.js 部署
// import { serve } from '@hono/node-server'
// serve(app, { port: 3000 })

// Cloudflare Workers 部署
// export default app

// Bun 部署
// export default { port: 3000, fetch: app.fetch }

// Deno 部署
// Deno.serve(app.fetch)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

2.2 Edge 优先的设计理念 ​

┌─────────────────────────────────────────────────────────────┐
│                  传统框架 vs Edge 优先框架                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  传统框架(Express / Fastify / NestJS):                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 设计前提:长生命周期的 Node.js 进程                  │   │
│  │ • 启动时做大量初始化(连接池、预编译、加载插件)     │   │
│  │ • 内存中维护连接池和缓存                              │   │
│  │ • 假设服务器始终在运行                                │   │
│  │                                                      │   │
│  │ 问题:Edge 环境没有"长生命周期"                      │   │
│  │ • Cloudflare Workers:按请求计费,冷启动             │   │
│  │ • Vercel Edge Functions:内存限制 128KB              │   │
│  │ • AWS Lambda:每次调用可能触发新实例                 │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Hono 的 Edge 优先设计:                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ • 极小的包体积(14KB)→ 冷启动极快                  │   │
│  │ • 零初始化开销 → 导入即用                            │   │
│  │ • 无连接池 → 每次请求独立                            │   │
│  │ • 路由在创建时编译为 Trie 树 → O(n) 匹配时间        │   │
│  │ • 使用 Web Standard API → 天然跨平台                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

2.3 RPC 模式——类型安全的客户端调用 ​

这是 Hono 最被低估的特性:通过 hc 客户端,在服务端和客户端之间共享类型定义,实现端到端的类型安全。

typescript
// 服务端代码:server.ts
import { Hono } from 'hono'

const app = new Hono()
  .get('/api/users', (c) => {
    return c.json([
      { id: 1, name: 'Alice', email: 'alice@example.com' },
      { id: 2, name: 'Bob', email: 'bob@example.com' },
    ])
  })
  .get('/api/users/:id', (c) => {
    const id = c.req.param('id')
    return c.json({ id: Number(id), name: 'Alice', email: 'alice@example.com' })
  })
  .post('/api/users', async (c) => {
    const body = await c.req.json<{ name: string; email: string }>()
    return c.json({ id: 3, ...body }, 201)
  })
  .delete('/api/users/:id', (c) => {
    return c.json({ success: true })
  })

// 导出类型供客户端使用
export type AppType = typeof app
export default app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typescript
// 客户端代码:client.ts —— 完全类型安全
import { hc } from 'hono/client'
import type { AppType } from './server' // 仅导入类型!

const client = hc<AppType>('http://localhost:3000')

// ✅ 所有调用都是类型安全的
const users = await client.api.users.$get()
//    ^? { id: number; name: string; email: string }[]

const user = await client.api.users[':id'].$get({ param: { id: '1' } })
//    ^? { id: number; name: string; email: string }

const newUser = await client.api.users.$post({
  json: { name: 'Charlie', email: 'charlie@example.com' }
})
//    ^? { id: number; name: string; email: string }

// ❌ 编译错误:缺少必填字段
// await client.api.users.$post({ json: { name: 'Charlie' } })
//                       Property 'email' is missing in type...

// ❌ 编译错误:错误的路径参数
// await client.api.users[':id'].$get({ param: { userId: '1' } })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────────────────┐
│                    Hono RPC 类型流转                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  服务端定义                    客户端使用                    │
│  ┌──────────────────────┐    ┌──────────────────────┐      │
│  │ c.json({             │    │ client.api.users     │      │
│  │   id: 1,             │    │   .$get()            │      │
│  │   name: 'Alice'      │    │                      │      │
│  │ })                   │    │ 返回类型自动推导:    │      │
│  │                      │    │ { id: number;        │      │
│  │ 路由: /api/users     │    │   name: string }     │      │
│  │ 方法: GET            │    └──────────────────────┘      │
│  └──────────────────────┘                                   │
│                                                             │
│  无需:                                                      │
│  • tRPC 的 codegen 步骤                                     │
│  • OpenAPI 的 codegen 步骤                                  │
│  • GraphQL 的 schema 定义                                   │
│  • 手动维护类型文件                                          │
│                                                             │
│  仅需:导出 typeof app,导入为类型参数                       │
│                                                             │
│  限制:                                                      │
│  • 运行时是标准 HTTP fetch,不是二进制协议                   │
│  • 相比 tRPC 缺少 middleware、subscriptions 等高级功能      │
│  • 适合中小型项目;大型 monorepo 可能需要更强的代码生成      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

2.4 中间件生态与 React Server Components 集成 ​

typescript
// Hono 中间件生态(内置 + 官方)
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { jwt } from 'hono/jwt'
import { logger } from 'hono/logger'
import { compress } from 'hono/compress'
import { prettyJSON } from 'hono/pretty-json'
import { secureHeaders } from 'hono/secure-headers'
import { etag } from 'hono/etag'
import { timeout } from 'hono/timeout'

const app = new Hono()

// 全局中间件
app.use('*', cors())
app.use('*', logger())
app.use('*', compress())
app.use('*', secureHeaders())

// 路由级 JWT 认证
const api = new Hono()
api.use('/admin/*', jwt({ secret: process.env.JWT_SECRET! }))
api.get('/admin/dashboard', (c) => {
  const payload = c.get('jwtPayload')
  return c.json({ message: `Welcome ${payload.sub}` })
})

app.route('/api', api)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typescript
// Hono 与 React Server Components (RSC) 集成
// Hono 可以作为 Next.js / RSC 的轻量级替代方案

import { Hono } from 'hono'
import { renderToString } from 'react-dom/server'

const app = new Hono()

app.get('/page', (c) => {
  // 服务端渲染 React 组件
  const html = renderToString(<UserList users={users} />)

  // 返回完整 HTML
  return c.html(`<!DOCTYPE html>
<html>
  <body>
    <div id="root">${html}</div>
    <script src="/client.js"></script>
  </body>
</html>`)
})

// Hono 的 JSX 支持(使用 hono/jsx 替代 React)
// 对于简单页面,可以直接用 Hono 内置的 JSX,零依赖
app.get('/simple', (c) => {
  return c.html(
    <Layout title="Dashboard">
      <h1>Welcome</h1>
      <p>Rendered with Hono JSX (no React needed)</p>
    </Layout>
  )
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

第3部分:NestJS 架构解析 ​

3.1 依赖注入容器的设计 ​

┌─────────────────────────────────────────────────────────────┐
│                    NestJS 依赖注入容器                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Module(模块)                     │   │
│  │  逻辑边界——将相关功能组织在一起                       │   │
│  │  ┌───────────────────────────────────────────────┐  │   │
│  │  │ @Module({                                     │  │   │
│  │  │   imports: [DatabaseModule, CacheModule],     │  │   │
│  │  │   controllers: [UserController],              │  │   │
│  │  │   providers: [UserService, UserRepository],   │  │   │
│  │  │   exports: [UserService],                     │  │   │
│  │  │ })                                            │  │   │
│  │  └───────────────────────────────────────────────┘  │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  DI 容器启动流程:                                           │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 1. 扫描所有 @Module 装饰的类                        │   │
│  │ 2. 构建模块依赖图(imports 关系)                   │   │
│  │ 3. 拓扑排序 → 确定模块初始化顺序                    │   │
│  │ 4. 逐个实例化 providers(解析构造函数参数类型)     │   │
│  │ 5. 缓存单例 → 后续注入直接返回已缓存的实例          │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  三种 Provider 作用域:                                      │
│  • DEFAULT(单例)—— 整个应用共享一个实例                   │
│  • REQUEST —— 每个 HTTP 请求创建一个实例                    │
│  • TRANSIENT —— 每次注入创建一个新实例                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
typescript
// NestJS 依赖注入实战
import { Injectable, Module, Controller, Get, Param } from '@nestjs/common'

// Step 1: 定义可注入的服务
@Injectable()
class UserRepository {
  async findById(id: number) {
    return { id, name: 'Alice', email: 'alice@example.com' }
  }
}

@Injectable()
class UserService {
  // 构造函数注入——NestJS 自动解析
  constructor(private readonly repo: UserRepository) {}

  async getUser(id: number) {
    const user = await this.repo.findById(id)
    if (!user) throw new NotFoundException(`User ${id} not found`)
    return user
  }
}

// Step 2: 控制器消费服务
@Controller('users')
class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(':id')
  async getUser(@Param('id') id: number) {
    return this.userService.getUser(id)
  }
}

// Step 3: 模块注册一切
@Module({
  controllers: [UserController],
  providers: [UserService, UserRepository],
  exports: [UserService], // 允许其他模块注入 UserService
})
export class UserModule {}

// 对比 Express —— 同样的功能需要手动管理依赖:
// const repo = new UserRepository()
// const service = new UserService(repo)
// const controller = new UserController(service)
// app.get('/users/:id', (req, res) => controller.getUser(req.params.id))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

3.2 Guards / Interceptors / Pipes / Filters——管道模型 ​

┌─────────────────────────────────────────────────────────────┐
│              NestJS 请求处理管道(洋葱模型变体)              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  请求进入                                                    │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Middleware(中间件)                                  │   │
│  │ 全局 → 模块级 → 路由级                                │   │
│  │ 适用:CORS、Body 解析、请求日志                      │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Guards(守卫)                                        │   │
│  │ 决定"谁能访问"                                       │   │
│  │ 适用:认证、角色检查、功能开关                        │   │
│  │ 返回 true → 继续;返回 false → 403                   │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Interceptors(拦截器)——请求前                       │   │
│  │ 适用:请求转换、缓存检查、Metrics 开始计时          │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Pipes(管道)                                         │   │
│  │ 决定"数据长什么样"                                   │   │
│  │ 适用:参数验证、类型转换、默认值设置                 │   │
│  │ 如 @Body(new ValidationPipe()) → 自动 Zod 验证       │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Controller Handler(实际业务逻辑)                   │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Interceptors(拦截器)——响应后                       │   │
│  │ 适用:响应包装、序列化、Metrics 结束计时            │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ Exception Filters(异常过滤器)                      │   │
│  │ 捕获所有未处理的异常                                  │   │
│  │ 适用:统一错误格式、错误日志、错误分类响应           │   │
│  └─────────────────────────────────────────────────────┘   │
│     ↓                                                       │
│  响应返回                                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
typescript
// 完整示例:管道模型协同工作
import {
  Controller, Get, Post, Body, Param, Query,
  UseGuards, UseInterceptors, UsePipes, UseFilters,
  ValidationPipe, ParseIntPipe,
} from '@nestjs/common'

@Controller('users')
@UseGuards(AuthGuard)          // 所有路由都需要认证
@UseInterceptors(LoggingInterceptor) // 所有路由记录性能
export class UserController {

  @Get()
  @UseGuards(RolesGuard)       // 额外要求 admin 角色
  @UsePipes(new ValidationPipe({ transform: true }))
  async listUsers(
    @Query('page', ParseIntPipe) page: number = 1, // 自动转 number
    @Query('limit', ParseIntPipe) limit: number = 10,
  ) {
    return this.userService.paginate(page, limit)
  }

  @Post()
  @UsePipes(new ValidationPipe())
  async createUser(@Body() dto: CreateUserDto) {
    // dto 已经被验证和转换
    return this.userService.create(dto)
  }

  @Get(':id')
  async getUser(@Param('id', ParseIntPipe) id: number) {
    // id 保证是 number 类型
    return this.userService.findById(id)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

3.3 微服务传输层与适配器模式 ​

┌─────────────────────────────────────────────────────────────┐
│                NestJS 微服务传输层抽象                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  同一套 Controller 代码,切换传输层:                         │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              @Controller('users')                    │   │
│  │              @MessagePattern('users.get')            │   │
│  │              getUser(@Payload() id: number) {}       │   │
│  └─────────────────────────┬───────────────────────────┘   │
│                            ↓                                │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │   TCP    │  │  Redis   │  │  gRPC    │  │  Kafka   │   │
│  │ Transport│  │ Transport│  │ Transport│  │ Transport│   │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘   │
│                                                             │
│  其他支持的传输层:                                           │
│  • MQTT(物联网场景)                                       │
│  • NATS(云原生消息系统)                                   │
│  • RabbitMQ(AMQP 协议)                                    │
│  • WebSocketGateway(实时通信)                             │
│                                                             │
│  关键:业务代码不感知传输层——切换传输层无需修改              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typescript
// NestJS 与 Express / Fastify 的适配器模式
// NestJS 不绑定 HTTP 引擎,通过适配器抽象

import { NestFactory } from '@nestjs/core'
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify'

// 默认:Express 适配器
const app = await NestFactory.create(AppModule)
app.listen(3000)

// 切换为 Fastify 适配器(仅需改两行代码)
const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter(),
)
app.listen(3000)

// 微服务模式:混合 HTTP + 消息队列
const app = await NestFactory.create(AppModule)

// 同时监听 HTTP 和 Redis 微服务
app.connectMicroservice({
  transport: Transport.REDIS,
  options: { host: 'localhost', port: 6379 },
})

await app.startAllMicroservices()
await app.listen(3000)
// 现在 /users/:id 的 GET 请求走 HTTP
// 'users.get' 的消息走 Redis Pub/Sub
// Controller 代码完全一样,只是装饰器不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

第4部分:Nitro——Nuxt 的服务器引擎 ​

4.1 unjs 生态全景 ​

┌─────────────────────────────────────────────────────────────┐
│                    unjs 生态与 Nitro 的定位                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                    Nitro                             │   │
│  │          (Nuxt 的服务器引擎 + 独立可用)              │   │
│  │                                                      │   │
│  │  定位:通用服务端框架,不是"Web 框架"                 │   │
│  │  解决:打包、部署、存储、路由——不关心你是用          │   │
│  │        Express 还是 Hono 还是纯 node:http            │   │
│  └──────────┬──────────┬──────────┬────────────────────┘   │
│             ↓          ↓          ↓                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────────┐     │
│  │   h3     │  │uncrypto  │  │     unstorage         │     │
│  │HTTP 框架│  │Web Crypto│  │  存储层抽象            │     │
│  │(hono风格)│  │polyfill  │  │  50+ 驱动              │     │
│  └──────────┘  └──────────┘  │  FS/Redis/VercelKV/   │     │
│                               │  CloudflareKV/Planet  │     │
│  ┌──────────┐  ┌──────────┐  │  Scale/MongoDB/...    │     │
│  │ consola  │  │  ofetch  │  └──────────────────────┘     │
│  │日志工具  │  │fetch封装 │                                │
│  └──────────┘  └──────────┘                                │
│                                                             │
│  其他 unjs 工具:                                            │
│  • unplugin(统一构建插件)  • unenv(环境 polyfill)       │
│  • untyped(运行时 Schema)   • unbuild(Rollup 封装)      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

4.2 跨平台部署 ​

typescript
// Nitro 的核心能力:一套代码,多处部署
// nitro.config.ts
import { defineNitroConfig } from 'nitropack/config'

export default defineNitroConfig({
  // 预设部署目标
  preset: 'cloudflare-pages', // 或:
  // 'node-server' | 'vercel' | 'vercel-edge' | 'netlify' |
  // 'netlify-edge' | 'aws-lambda' | 'cloudflare-module' |
  // 'deno-server' | 'bun' | 'deno-deploy' | 'firebase' |
  // 'heroku' | 'render' | 'stormkit' | 'iis' | ...

  // 文件路由(约定式路由)
  // routes/users/[id].get.ts  →  GET /users/:id
  // routes/users/index.post.ts →  POST /users
  // routes/api/[...].ts       →  /api/*  (catch-all)

  // 存储层(统一 API,自动适配环境)
  storage: {
    cache: { driver: 'redis', host: 'localhost' },
    // 在 Cloudflare 上自动切换为 Workers KV
    // 在 Vercel 上自动切换为 Vercel KV
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────────────────┐
│                 Nitro 部署抽象原理                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  开发者写的代码                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ // routes/api/hello.ts                              │   │
│  │ export default defineEventHandler((event) => {      │   │
│  │   return { hello: 'world' }                         │   │
│  │ })                                                  │   │
│  └─────────────────────────────────────────────────────┘   │
│                            ↓                                │
│  Nitro 构建时(Rollup + esbuild):                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ 1. 收集所有路由文件 → 生成路由映射表                │   │
│  │ 2. 按 preset 选择对应的 entry 模板                  │   │
│  │ 3. Tree-shaking:只打包使用到的代码                 │   │
│  │ 4. 环境 polyfill(如用 unenv 为 Edge 补充 polyfill)│   │
│  │ 5. 生成平台适配层代码                                │   │
│  └─────────────────────────────────────────────────────┘   │
│                            ↓                                │
│  输出(按 preset 不同):                                    │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ node-server  → dist/server/index.mjs                │   │
│  │ vercel       → .vercel/output/functions/index.func  │   │
│  │ cloudflare   → dist/_worker.js                      │   │
│  │ aws-lambda   → dist/lambda/index.mjs                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  关键:你的业务代码不需要 import Node.js 或 Cloudflare 的 API│
│  Nitro 通过 unstorage / h3 / ofetch 提供了统一的抽象层     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

4.3 文件路由约定与存储层抽象 ​

typescript
// Nitro 的文件路由系统
// 目录结构即 API 结构

// routes/
// ├── users/
// │   ├── index.get.ts        → GET /users
// │   ├── index.post.ts       → POST /users
// │   ├── [id].get.ts         → GET /users/:id
// │   └── [id].delete.ts      → DELETE /users/:id
// ├── posts/
// │   └── [...slug].get.ts    → GET /posts/* (catch-all)
// └── api/
//     └── search.get.ts       → GET /api/search?q=...

// routes/users/index.get.ts
export default defineEventHandler(async (event) => {
  // 获取 query 参数(自动类型校验)
  const query = getQuery(event)
  const page = Number(query.page) || 1
  const limit = Number(query.limit) || 10

  // 使用统一存储 API
  const cacheKey = `users:list:${page}:${limit}`
  const cached = await useStorage('cache').getItem(cacheKey)
  if (cached) return cached

  const users = await db.user.findMany({ skip: (page - 1) * limit, take: limit })

  // 缓存 60 秒
  await useStorage('cache').setItem(cacheKey, users, { ttl: 60 })

  return users
})

// routes/users/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')

  // 验证 ID 格式
  if (!id || !/^\d+$/.test(id)) {
    throw createError({ statusCode: 400, message: 'Invalid user ID' })
  }

  const user = await db.user.findUnique({ where: { id: Number(id) } })
  if (!user) {
    throw createError({ statusCode: 404, message: 'User not found' })
  }

  return user
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

第5部分:Bun + Elysia ​

5.1 Bun 的原生性能优势 ​

┌─────────────────────────────────────────────────────────────┐
│              Bun 运行时架构 vs Node.js                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Node.js                        Bun                         │
│  ┌────────────────────┐      ┌────────────────────┐        │
│  │   JavaScript 代码   │      │   JavaScript 代码   │        │
│  └─────────┬──────────┘      └─────────┬──────────┘        │
│            ↓                           ↓                    │
│  ┌────────────────────┐      ┌────────────────────┐        │
│  │   V8 (C++)         │      │ JavaScriptCore (C)  │        │
│  │   JIT 编译         │      │ JIT 编译 (Safari 引擎)│       │
│  └─────────┬──────────┘      └─────────┬──────────┘        │
│            ↓                           ↓                    │
│  ┌────────────────────┐      ┌────────────────────┐        │
│  │   libuv (C)        │      │   Zig HTTP/TCP/FS   │        │
│  │   事件循环          │      │  (无 libuv 中间层)  │        │
│  └─────────┬──────────┘      └─────────┬──────────┘        │
│            ↓                           ↓                    │
│  ┌────────────────────┐      ┌────────────────────┐        │
│  │   OS Kernel        │      │   OS Kernel        │        │
│  └────────────────────┘      └────────────────────┘        │
│                                                             │
│  Bun 的性能来源:                                            │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ • JavaScriptCore → 比 V8 启动快 4 倍                │   │
│  │ • Zig 实现 → 无 C++ 抽象开销,更接近系统调用        │   │
│  │ • 内置工具链 → 打包(bun build)、测试(bun test)、   │   │
│  │   包管理(bun install) 都用原生代码实现              │   │
│  │ • 内置 SQLite → 嵌入式数据库零配置                  │   │
│  │ • 原生 HTTP 服务器 → 比 Node.js http 快             │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  基准参考(简单路由"Hello World"):                         │
│  Bun.serve:      ~150K req/s                                │
│  Hono + Bun:    ~130K req/s                                 │
│  Elysia + Bun:  ~120K req/s                                 │
│  Fastify + Node: ~65K req/s                                 │
│  Express + Node: ~15K req/s                                 │
│                                                             │
│  ⚠️ 注意:Hello World 不代表真实应用性能                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

5.2 Elysia——类型安全的端到端设计 ​

typescript
import { Elysia, t } from 'elysia'

// Elysia 的核心理念:类型安全不依赖 codegen
const app = new Elysia()
  // Schema 定义直接内联,既做验证又驱动类型推导
  .get('/users', async ({ query }) => {
    // query.page 自动推导为 number | undefined
    return { users: [], total: 0 }
  }, {
    query: t.Object({
      page: t.Optional(t.Numeric()),  // 自动将 string 转为 number
      limit: t.Optional(t.Numeric({ default: 10 })),
    }),
    response: t.Object({
      users: t.Array(t.Object({
        id: t.Number(),
        name: t.String(),
      })),
      total: t.Number(),
    }),
  })

  .post('/users', async ({ body, set }) => {
    set.status = 201
    return { id: 1, ...body }
  }, {
    body: t.Object({
      name: t.String({ minLength: 2, maxLength: 50 }),
      email: t.String({ format: 'email' }),
    }),
  })

  // Guard——分组应用中间件和 Schema
  .guard({
    beforeHandle: async ({ headers }) => {
      const token = headers.authorization
      if (!token) throw new Error('Unauthorized')
    }
  }, (app) =>
    app
      .get('/admin/stats', () => ({ users: 100, posts: 500 }))
      .delete('/users/:id', ({ params }) => {
        return { deleted: params.id }
      }, {
        params: t.Object({ id: t.Numeric() }),
      })
  )

  .listen(3000)

// 自动生成类型——无需导出 typeof app
export type App = typeof app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

5.3 与 Node.js 生态的兼容性现状 ​

┌─────────────────────────────────────────────────────────────┐
│                Bun 的 Node.js 兼容性评估                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✅ 完全兼容:                                               │
│  • 大部分 npm 包(Bun 实现了 Node.js API polyfill)         │
│  • CommonJS + ESM(透明互操作)                              │
│  • TypeScript(开箱即用,零配置)                            │
│  • JSX/TSX(内置支持)                                       │
│  • Path / fs / crypto / buffer 等核心模块                    │
│  • package.json 所有字段                                    │
│                                                             │
│  ⚠️ 部分兼容 / 需要测试:                                    │
│  • 使用 node-gyp 的原生 C++ addon(如 bcrypt、better-sqlite3)│
│    → Bun 提供了原生替代品(Bun.password、bun:sqlite)       │
│  • 依赖 V8 特定行为的库(如 heapdump)                       │
│  • Node.js 的 Worker Threads(Bun 有自己的 Worker 实现)    │
│  • 极深的 Node.js 内部 API 依赖(如 v8.serialize)           │
│                                                             │
│  ❌ 目前不推荐用于:                                          │
│  • 依赖大量 node-gyp 原生模块的遗留项目                      │
│  • 使用 Node.js 专有 API 的基础设施工具                     │
│  • 需要极端稳定性的金融/医疗核心系统                          │
│                                                             │
│  趋势:Bun 的兼容性在快速改善,1.0 之后 npm 兼容率 > 90%     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

第6部分:框架选型决策树 ​

6.1 按场景推荐 ​

┌─────────────────────────────────────────────────────────────┐
│                    框架选型决策树                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题 1:项目规模和团队大小?                                │
│                                                             │
│  小型 API / 个人项目                                         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ → 需要部署到 Edge/Serverless 吗?                    │   │
│  │   YES → Hono(极致轻量 + 多平台部署)               │   │
│  │   NO  → 追求极致性能吗?                            │   │
│  │          YES → Elysia + Bun                          │   │
│  │          NO  → Fastify(平衡性能与生态)            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  中型项目 / 5-10 人团队                                      │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ → TypeScript 优先吗?                               │   │
│  │   YES → Fastify + Zod + @fastify/swagger           │   │
│  │         或 NestJS(如果团队熟悉装饰器模式)          │   │
│  │   NO  → Express(生态最丰富,团队学习成本最低)     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  大型企业 / 10+ 人团队                                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ → 需要强架构约束吗?                                 │   │
│  │   YES → NestJS(DI + 模块化 + 管道模型)           │   │
│  │         选 Fastify 适配器代替 Express(性能提升)    │   │
│  │   NO  → Fastify + 分层架构 + 团队规范               │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  全栈项目 / Nuxt 项目                                        │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ → 使用 Nuxt 吗?                                     │   │
│  │   YES → Nitro(作为 Nuxt 的服务器引擎,天然集成)    │   │
│  │   NO  → Next.js / Remix(前端框架自带服务端能力)    │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Serverless / Edge 优先                                      │
│  ┌─────────────────────────────────────────────────────┐   │
│  │ → Hono(专门为此设计)                               │   │
│  │   也可考虑 Nitro(部署抽象更丰富)                   │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

6.2 六大维度量化对比 ​

┌─────────────────────────────────────────────────────────────┐
│                现代 Node.js 框架六维对比                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  维度                                                       │
│  │              │Express│Fastify│NestJS │ Hono  │Elysia│    │
│  ├──────────────┼───────┼───────┼───────┼───────┼──────┤    │
│  │ 吞吐量(req/s)│  ⭐⭐   │ ⭐⭐⭐⭐ │ ⭐⭐⭐  │ ⭐⭐⭐⭐⭐│ ⭐⭐⭐⭐ │    │
│  │ TS 类型安全  │  ⭐⭐   │ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐│ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐ │    │
│  │ 生态 & 插件  │ ⭐⭐⭐⭐⭐│ ⭐⭐⭐⭐ │ ⭐⭐⭐⭐⭐│ ⭐⭐⭐  │ ⭐⭐   │    │
│  │ 学习曲线    │ ⭐(平) │ ⭐⭐   │ ⭐⭐⭐⭐ │ ⭐(平) │ ⭐⭐   │    │
│  │ 架构治理    │  ⭐⭐   │ ⭐⭐⭐  │ ⭐⭐⭐⭐⭐│ ⭐⭐   │ ⭐⭐   │    │
│  │ 跨平台部署  │  ⭐⭐⭐  │ ⭐⭐⭐  │ ⭐⭐⭐  │ ⭐⭐⭐⭐⭐│  ⭐    │    │
│                                                             │
│  吞吐量说明:                                                │
│  • Elysia + Bun 的原始吞吐最高                               │
│  • Hono 在 Edge 环境的冷启动速度最快 (~5ms)                  │
│  • Fastify 在 Node.js 上吞吐最好                             │
│  • NestJS 的 DI 容器带来小幅性能开销 (~10%)                  │
│  • Express 在 raw 对比中垫底,但真实瓶颈通常在 I/O          │
│                                                             │
│  ⚠️ 核心提醒:对于 99% 的应用,数据库查询延迟                 │
│  (5-50ms)远大于框架开销(0.1-1ms)。                        │
│  不要为了框架基准数字而舍弃架构合理性。                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

6.3 团队学习成本与生态成熟度评估 ​

typescript
// 各框架的典型学习路径和"上手时间"
// 以下为经验估计,以具有 Node.js 基础的开发者为参考

// ┌─────────────────────────────────────────────────────────┐
// │ 框架        │ 上手时间    │ 精通时间    │ 关键难点       │
// ├─────────────────────────────────────────────────────────┤
// │ Express     │ 1-2 天     │ 2-4 周     │ 架构治理靠自己  │
// │ Fastify     │ 2-4 天     │ 4-8 周     │ 插件封装设计    │
// │ Hono        │ 1-2 天     │ 2-4 周     │ Edge 限制理解   │
// │ NestJS      │ 1-2 周     │ 2-4 月     │ DI/装饰器/模块  │
// │ Elysia      │ 1-2 天     │ 2-4 周     │ Bun 生态稳定性  │
// │ Nitro       │ 2-4 天     │ 2-4 周     │ unjs 工具链理解 │
// └─────────────────────────────────────────────────────────┘

// 关键生态指标(截至 2026-07):
// ┌─────────────────────────────────────────────────────────┐
// │ 框架        │ GitHub Stars │ npm 周下载    │ 主要赞助     │
// ├─────────────────────────────────────────────────────────┤
// │ Express     │ ~65K        │ ~30M          │ OpenJS       │
// │ Fastify     │ ~32K        │ ~3M           │ OpenJS       │
// │ NestJS      │ ~68K        │ ~4M           │ 独立公司     │
// │ Hono        │ ~20K        │ ~500K         │ 社区驱动     │
// │ Elysia      │ ~22K        │ ~50K          │ 社区驱动     │
// │ Nitro       │ ~6K         │ ~500K         │ Nuxt Labs    │
// └─────────────────────────────────────────────────────────┘

// 选型建议权重分配(主观建议):
const DECISION_WEIGHTS = {
  teamSize: 0.25,        // 团队规模越大,越需要 NestJS 的约束
  projectLifespan: 0.20, // 长期项目 → 生态成熟度优先
  perfRequirements: 0.15,// 高并发 → Fastify/Hono/Elysia
  tsAdoption: 0.15,      // TypeScript 优先 → 排除 Express
  deployTarget: 0.15,    // Edge → Hono;自有服务器 → 任意
  learningBudget: 0.10,  // 学习预算少 → Express/Hono
} as const
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

核心总结 ​

总结1:框架选择的本质不是性能 ​

Hello World 基准中 Express 和 Hono 可能相差 10 倍,但真实应用中数据库查询(5-50ms)远大于框架开销(0.1-1ms)。选框架时,架构合理性、团队熟悉度和生态成熟度的权重应远高于原始性能基准。

总结2:每个框架的"甜点区" ​

  • Express:学习 HTTP 和中间件概念的最佳教学工具,中小型 API 和快速原型的不二之选。但大型项目需要自己建立治理体系。
  • Fastify:新项目最佳的"默认选择"。Schema 驱动带来免费的类型安全 + OpenAPI 文档 + 高性能序列化,代价是灵活性略低于 Express。
  • Hono:如果目标环境是 Edge 或 Serverless,Hono 几乎是唯一正解。14KB 的体积和跨运行时能力让它在这个赛道上没有真正的对手。
  • NestJS:10 人以上团队、预期维护 3 年以上的企业级项目。DI 容器和模块化提供了天然的架构约束,但学习成本显著高于其他框架。
  • Elysia + Bun:追求极致性能和开发者体验的项目,但生态成熟度仍落后于 Node.js 阵营。适合新项目,不适合迁移现有 Node.js 系统。
  • Nitro:如果你已经在用 Nuxt,Nitro 是"默认自带"的服务器引擎。它的跨平台部署抽象是独特价值,适合需要多平台部署的全栈项目。

总结3:不选择也是选择 ​

不选择框架(直接用 node:http)在以下场景是合理的:构建框架本身、编写 CLI 工具、开发基础设施组件(代理、网关)。但一旦涉及业务逻辑,就应该选择一个框架——否则你最终会实现一个非标准的、未经测试的"自己的框架"。

总结4:适配器模式是架构共识 ​

注意 NestJS 可以切换 Express/Fastify 适配器,Fastify 有 Express 兼容层,Nitro 可以包装任何 runtime——现代框架越来越倾向于"不绑定引擎"。这个趋势说明:HTTP 引擎的选择和架构模式的选择应该是正交的。


章节测试 ​

测试1:Fastify 插件封装 ​

Fastify 的 register 和 register(fp(...)) 有什么区别?分别在什么场景下使用?

测试2:Schema 驱动的序列化 ​

为什么 Fastify 的 Schema 驱动序列化比 JSON.stringify 快 2-3 倍?原理是什么?

测试3:Hono 跨运行时 ​

Hono 如何做到同一套代码运行在 Node.js、Deno、Bun 和 Cloudflare Workers 上?核心设计决策是什么?

测试4:NestJS 管道模型 ​

请描述 NestJS 请求处理管道中 Guard、Interceptor、Pipe、Filter 的执行顺序和各自职责。

测试5:框架选型 ​

一个 3 人团队要构建一个部署到 Cloudflare Workers 的 API,对 TypeScript 类型安全有较高要求。你会推荐哪个框架?为什么?

测试6:Bun 的兼容性 ​

Bun 声称兼容 Node.js,但为什么使用 node-gyp 的原生 C++ 模块仍然可能无法运行?


参考答案 ​

测试1答案 ​

答案:

  • register(plugin):创建子作用域,插件的装饰器、Hook 仅在子作用域内有效。适合业务路由(独立封装,避免命名冲突)。
  • register(fp(plugin)):不创建子作用域,装饰器挂载到父作用域。适合基础设施组件(数据库连接、认证中间件、日志工具等需要全局共享的)。

测试2答案 ​

答案:JSON Schema 提供了完整的类型信息。Fastify 在应用启动时,根据 Schema 预编译出专门的序列化函数——这个函数"已经知道"对象的每个属性类型,不需要运行时做 typeof 检查和动态字符串拼接。相当于从"解释执行"变成了"编译执行"。

测试3答案 ​

答案:Hono 完全基于 Web Standard API(Request/Response/Headers/URL),不依赖 Node.js 特有 API。每个运行时只需要一个最小的适配层(将运行时的请求转换为标准 Request 对象)。因为 Web Standard API 已成为所有现代 JavaScript 运行时的共同基础,Hono 自然获得了跨平台能力。

测试4答案 ​

答案:执行顺序和职责:

  1. Middleware:最外层,CORS、Body 解析、请求日志
  2. Guard:认证和授权,"谁能访问"——返回 true 继续,false 返回 403
  3. Interceptor(请求前):请求转换、缓存检查
  4. Pipe:参数验证和类型转换,"数据长什么样"
  5. Handler:实际业务逻辑
  6. Interceptor(响应后):响应包装、序列化
  7. Exception Filter:捕获未处理异常,统一错误响应格式

测试5答案 ​

答案:推荐 Hono。

  • Cloudflare Workers 是 Edge 环境,Hono 为此专门设计(14KB,冷启动 < 5ms)
  • Hono 内置了优秀的 TypeScript 类型推导
  • Hono 的 hc RPC 客户端在小型团队中提供了远超投入的端到端类型安全
  • 3 人团队不需要 NestJS 级别的架构约束
  • Fastify 和 Express 不适合 Workers 环境(体积大,依赖 Node.js API)

测试6答案 ​

答案:node-gyp 编译的模块是 C++ 代码通过 V8 的 C++ API(N-API / node-addon-api)直接与 Node.js 运行时交互的。Bun 使用 JavaScriptCore 而非 V8,其内部 C API 完全不同。虽然 Bun 实现了 Node.js 的 JavaScript API 层(如 fs、http),但无法兼容 V8 的 C++ 绑定接口。因此依赖 C++ addon 的包(如 bcrypt 的原生版本)在 Bun 上无法运行——需要使用纯 JavaScript 实现或 Bun 提供的原生替代。


相关笔记 ​

  • [[03-express-deep-dive]] — Express 中间件洋葱模型
  • [[00-overview]] — Node.js 运行时全景与学习路线
  • [[02-async-patterns-and-error-handling]] — 异步模式与错误处理
  • [[../05-backend-engineering/11-architecture-patterns]] — 后端架构模式
  • [[../05-backend-engineering/13-system-design]] — 系统设计原则

下一步学习 ​

  • [ ] 阅读 API 设计与 REST 原则 — 掌握 API 设计最佳实践
  • [ ] 用 Fastify 实现一个带 Zod 验证、Swagger 文档的 CRUD API
  • [ ] 用 Hono 部署一个 Cloudflare Workers 服务,体验 Edge 部署
  • [ ] 阅读 NestJS 官方文档的 Fundamentals 章节,完成一个 Module 拆分练习
  • [ ] 用 Elysia + Bun 搭建一个 bench 项目,亲自对比框架间性能差异

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇5. Express 高级模式与生产实践 / Express Advanced Patterns and Production Practices
下一篇7. RESTful API 设计原则与实践 / RESTful API Design Principles and Practice

持续记录,持续成长

Copyright © Tidenflow