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 endimport { 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() 构建可扩展架构。