目录

Hono 最佳实践

快速入门

安装与版本

Hono 是一个小巧、简洁、极速的 Web 框架,基于 Web 标准构建。它可以在任何 JavaScript 运行时运行。

# npm
npm create hono@latest

# yarn
yarn create hono

# pnpm
pnpm create hono@latest

# bun
bun create hono@latest

# deno
deno init
deno add npm:hono

最小示例

import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Hello Hono!'))

export default app

核心概念

极速路由

Hono 使用 RegExpRouter - 无需线性循环。路由通过正则表达式匹配,性能极佳。

import { Hono } from 'hono'

const app = new Hono()

// 基础路由
app.get('/users', (c) => c.json({ users: [] }))

// 路径参数(完整类型推导)
app.get('/users/:id', (c) => {
  const { id } = c.req.param()
  return c.json({ user: { id } })
})

// 可选参数
app.get('/posts/:slug?', (c) => {
  const { slug } = c.req.param()
  return c.json({ slug: slug ?? 'default' })
})

// 正则约束
app.get('/items/:date{[0-9]+}', (c) => {
  return c.text('Date pattern matched')
})

Context 对象

Context 对象(c)为每个请求实例化,提供所有必要的工具方法:

app.get('/example', (c) => {
  // 请求访问
  const method = c.req.method
  const path = c.req.path
  const query = c.req.query('name')
  const header = c.req.header('Authorization')

  // 响应辅助方法
  return c.json({ message: 'OK' }, 200)
  // 或者: c.text(), c.html(), c.body()

  // 设置状态码
  c.status(201)

  // 设置响应头
  c.header('X-Custom', 'value')

  // 404 响应
  return c.notFound()
})

中间件模式

中间件执行顺序

中间件严格按注册顺序执行,像洋葱一样层层包裹:

middleware 1 start
  middleware 2 start
    handler
  middleware 2 end
middleware 1 end
import { logger } from 'hono/logger'

app.use(logger())  // 所有路由最先执行
app.use(authenticate())  // 次之执行

app.get('/foo', (c) => c.text('foo'))

路径特定中间件

// 仅应用于特定路径
app.use('/api/*', cors())

// 应用于特定方法 + 路径
app.post('/users/*', basicAuth())

使用 createMiddleware 创建自定义中间件

使用 createMiddleware() 创建可复用、类型安全的中间件:

import { createMiddleware } from 'hono/factory'

const logger = createMiddleware(async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})

// 带类型变量的中间件
const authMiddleware = createMiddleware<{
  Variables: { user: { id: string; name: string } }
}>(async (c, next) => {
  const user = await verifyToken(c)
  c.set('user', user)
  await next()
})

// 链式使用时自动合并类型
app
  .use(authMiddleware)
  .get('/', (c) => {
    const user = c.var.user  // 完整类型推导
    return c.json({ name: user.name })
  })

在处理器之后修改响应

const addTimestamp = createMiddleware(async (c, next) => {
  await next()
  const originalText = await c.res.text()
  c.res = new Response(`[${Date.now()}] ${originalText}`)
})

路由架构

使用 app.route() 扩展应用

对于大型应用,创建独立的路由文件并挂载:

// routes/users.ts
import { Hono } from 'hono'
export const users = new Hono()

users.get('/', (c) => c.json({ users: [] }))
users.post('/', (c) => c.json({ id: '1' }, 201))

// routes/books.ts
import { Hono } from 'hono'
export const books = new Hono()

books.get('/', (c) => c.json({ books: [] }))

// index.ts
import { Hono } from 'hono'
import { users } from './routes/users'
import { books } from './routes/books'

const app = new Hono()

app.route('/api/users', users)
app.route('/api/books', books)

export default app

基础路径

const api = new Hono().basePath('/api')

api.get('/posts', (c) => c.json({ posts: [] }))

app.route('/api', api)

RPC 端到端类型安全

服务端设置

导出应用类型供客户端类型推导:

import { Hono } from 'hono'
import { cors } from 'hono/cors'

const app = new Hono()

app.use('/api/*', cors())

app.get('/api/posts', (c) => {
  return c.json([{ id: 1, title: 'Hello' }])
})

app.post('/api/posts', async (c) => {
  const { title } = await c.req.json()
  return c.json({ id: 2, title }, 201)
})

export type AppType = typeof app
export default app

客户端使用

import { hc } from 'hono/client'

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

// 完全类型化的请求/响应
const res = await client.api.posts.$get()
const posts = await res.json()

// POST 带类型化的请求体
const newPost = await client.api.posts.$post({
  json: { title: 'New Post' }
})

状态码类型推导

显式指定状态码以获得类型推导:

// 推荐 - 状态码被类型化
app.get('/posts/:id', (c) => {
  const post = findPost(c.req.param('id'))
  if (!post) {
    return c.json({ error: 'Not found' }, 404)  // 类型为 404
  }
  return c.json(post)  // 类型为 200
})

// 使用 HTTPException 的替代方式
app.get('/posts/:id', (c) => {
  const post = findPost(c.req.param('id'))
  if (!post) {
    throw new HTTPException(404, { message: 'Not found' })
  }
  return c.json(post)
})

使用 Zod 进行验证

使用 @hono/zod-validator

import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const postSchema = z.object({
  title: z.string().min(1),
  body: z.string().optional(),
})

const route = app.post(
  '/posts',
  zValidator('json', postSchema),
  (c) => {
    const { title, body } = c.req.valid('json')
    return c.json({ id: crypto.randomUUID(), title, body }, 201)
  }
)

export type RouteType = typeof route

使用 @hono/standard-validator

支持 Zod、Valibot 和 ArkType 的统一 API:

import { sValidator } from '@hono/standard-validator'
import { z } from 'zod'

const schema = z.object({
  name: z.string(),
  age: z.number(),
})

app.post('/author', sValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  return c.json({ success: true, message: `${data.name} is ${data.age}` })
})

错误处理

HTTPException

import { HTTPException } from 'hono/http-exception'

app.post('/users', async (c) => {
  const body = await c.req.json()

  if (!body.email) {
    throw new HTTPException(400, { message: 'Email is required' })
  }

  const user = await createUser(body)
  return c.json(user, 201)
})

// 全局错误处理器
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return err.getResponse()
  }
  console.error(`${err}`)
  return c.text('Internal Server Error', 500)
})

// 404 处理器
app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404)
})

内置中间件

Hono 包含全面的内置中间件:

import {
  cors,
  logger,
  basicAuth,
  bearerAuth,
  jwt,
  compress,
  poweredBy,
  methodOverride,
} from 'hono/middleware'

常用中间件使用

import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { jwt } from 'hono/jwt'
import { compress } from 'hono/compress'

const app = new Hono()

app.use(logger())
app.use(compress())
app.use(cors({
  origin: ['https://example.com'],
  credentials: true,
}))

// JWT 认证
app.use('/api/*', jwt({
  secret: process.env.JWT_SECRET,
}))

// Basic 认证
app.use('/admin/*', basicAuth({
  username: 'admin',
  password: process.env.ADMIN_PASSWORD,
}))

多运行时部署

Hono 可无缝运行在不同运行时环境中:

Cloudflare Workers

import { Hono } from 'hono'
import { cors } from 'hono/cors'

const app = new Hono()

app.use('/*', cors())

app.get('/hello', (c) => {
  return c.json({
    message: 'Hello from Cloudflare Workers!',
    env: c.env.ASSETS,  // Cloudflare binding
  })
})

export default {
  fetch: app.fetch,
  scheduled: app.scheduled,
}

Deno

import { Hono } from 'https://deno.land/x/hono/mod.ts'

const app = new Hono()
app.get('/', (c) => c.text('Hello from Deno!'))

Deno.serve(app.fetch)

Bun

import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello from Bun!'))

export default {
  port: 3000,
  fetch: app.fetch,
}

Node.js(使用适配器)

import { Hono } from 'hono'
import { serve } from '@hono/node-server'
import { cors } from 'hono/cors'

const app = new Hono()
app.use('/*', cors())

app.get('/', (c) => c.text('Hello from Node.js!'))

serve({
  fetch: app.fetch,
  port: 3000,
})

性能优化

合理使用 Preset

Hono 提供不同预设,在体积和功能间取得平衡:

// hono/tiny - 最小,约 14KB
import { Hono } from 'hono/tiny'

// hono/base - 包含更多功能
import { Hono } from 'hono/base'

// hono - 全功能(默认)
import { Hono } from 'hono'

避免 Controller 类模式

// 推荐 - 正确的类型推导
app.get('/users/:id', (c) => {
  const { id } = c.req.param()
  return c.json({ id })
})

// 避免 - 丢失路径参数的类型推导
const getUser = (c) => c.json({ id: c.req.param('id') })
app.get('/users/:id', controller(getUser))

需要时使用 Factory 模式

如果偏好组织化的控制器,使用 factory.createHandlers()

import { createFactory } from 'hono/factory'

const factory = createFactory()

const handlers = factory.createHandlers(
  logger(),
  authenticate(),
  (c) => {
    return c.json(c.var.user)
  }
)

app.get('/profile', ...handlers)

测试

使用 app.request()

describe('API', () => {
  it('GET /posts 返回文章列表', async () => {
    const res = await app.request('/posts')
    expect(res.status).toBe(200)
    const body = await res.json()
    expect(body.posts).toBeDefined()
  })

  it('POST /posts 创建新文章', async () => {
    const res = await app.request('/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ title: 'Test' }),
    })
    expect(res.status).toBe(201)
  })

  it('模拟环境变量', async () => {
    const res = await app.request('/kv/test', {}, {
     Bindings: { MY_KV: { get: () => 'value' } },
    })
    expect(res.status).toBe(200)
  })
})

项目结构推荐

src/
├── index.ts              # 入口文件
├── routes/
│   ├── users.ts         # 用户路由
│   ├── posts.ts         # 文章路由
│   └── api/
│       └── v1.ts        # API v1
├── middleware/
│   ├── auth.ts
│   └── logger.ts
├── lib/
│   └── db.ts
└── types/
    └── index.ts         # 共享类型

总结

Hono 提供了一个轻量但强大的 Web 框架,具有以下特点:

  • 极速路由 - 通过 RegExpRouter 实现
  • 完整的 TypeScript 支持 - RPC 类型推导
  • 可组合的中间件 - 可预测的执行顺序
  • 多运行时支持(Cloudflare、Deno、Bun、Node.js、Lambda)
  • 内置中间件 - 处理常见任务(CORS、认证、日志)
  • Zod 验证集成 - 类型安全的输入验证

高效使用 Hono 的关键:充分利用 TypeScript 类型、智慧地组合中间件、使用 app.route() 构建可扩展架构。