夏日幽灵 サマーゴースト (2021)
1204 字
6 分钟
Hello, Hono
为什么选择 Hono.js
Hono 起初是为 Cloudflare Workers 设计的超轻量 Web 框架,现在已支持 Deno、Bun、Node.js、Vercel Edge、Lagon 等多种运行时。它兼具以下特性:
- 快速:核心仅 ~15KB,响应链路短,天然适合边缘侧(Edge)与无服务器函数。
- 兼容 Fetch 标准:上下文就是
Request/Response,易于在不同运行时迁移。 - TypeScript 友好:严格的类型推导,加上
hc/zod等生态可自动生成客户端与 OpenAPI。 - 中间件生态:内置 CORS、Basic Auth、JWT、Validator 等工具,也能继承 Express 风格中间件。
本文整理 Hono 在实际项目中的最佳实践,帮助你搭建稳定、可维护的微服务 API。
项目脚手架
推荐使用官方 CLI:
npm create hono@latest my-hono-app
cd my-hono-app
npm install
npm run dev
生成的项目默认使用 TypeScript 与 ESLint,可根据运行时切换 adapter:
cloudflare-workersbundenonode-server(Node.js 运行,配合 Fastly Compute、Lambda 等)
配置建议:
- 在
tsconfig.json开启strict,noUncheckedIndexedAccess。 - 配置
eslint-config-hono与typescript-eslint保证一致性。 - 使用
@hono/zod-validator做输入校验。
路由与模块化
Hono 按照路径注册 handler,建议按业务拆分:
import { Hono } from 'hono';
import { userRouter } from './routes/users';
import { authRouter } from './routes/auth';
const app = new Hono();
app.route('/auth', authRouter);
app.route('/users', userRouter);
export default app;
routes/users.ts:
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
export const userRouter = new Hono()
.get(
'/',
zValidator('query', z.object({ page: z.coerce.number().int().min(1).default(1) })),
async (c) => {
const { page } = c.req.valid('query');
const result = await c.get('services').user.list({ page });
return c.json(result);
}
)
.post(
'/',
zValidator('json', z.object({ email: z.string().email(), password: z.string().min(8) })),
async (c) => {
const payload = c.req.valid('json');
const user = await c.get('services').user.create(payload);
return c.json(user, 201);
}
);
要点:
- 尽量在路由文件中保持“瘦控制器”,把业务逻辑下沉到 service/domain 层。
- 通过
c.get()/c.set()将服务或依赖注入Context。 - 利用
zod校验 + 类型推导避免重复声明类型。
中间件与上下文
常用中间件示例:
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { poweredBy } from 'hono/powered-by';
import { secureHeaders } from 'hono/secure-headers';
app.use('*', poweredBy());
app.use('*', logger());
app.use('*', secureHeaders());
app.use(
'/api/*',
cors({
origin: ['https://app.example.com'],
allowMethods: ['GET', 'POST', 'PATCH', 'DELETE'],
allowHeaders: ['Authorization', 'Content-Type'],
})
);
依赖注入:
import type { Services } from './services';
type Bindings = {
SERVICES: Services;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use('*', async (c, next) => {
c.set('services', c.env.SERVICES);
await next();
});
执行顺序遵循注册顺序,注意在 Edge 环境中避免阻塞型操作(例如 crypto.randomUUID 可替代 uuid 包)。
错误处理
统一错误处理能提升 DX:
app.onError((err, c) => {
c.get('logger')?.error(err);
if (err instanceof HTTPException) {
return err.getResponse();
}
return c.json(
{
code: 'INTERNAL_ERROR',
message: '服务器开小差了',
},
500
);
});
app.notFound((c) =>
c.json(
{
code: 'NOT_FOUND',
message: `Route ${c.req.method} ${c.req.path} 不存在`,
},
404
)
);
- 使用
HTTPException抛出领域错误,例如throw new HTTPException(403, { message: 'Forbidden' })。 - 在日志中记录
requestId(可从 header 或crypto.randomUUID()生成)。 - Edge 平台通常有
ExecutionContext.waitUntil,可以结合app.use注入日志/埋点。
数据访问与性能
- Cloudflare D1、PlanetScale、Supabase 可通过
Context.env注入客户端。 - 在 Edge 环境使用
fetch调用内部服务时,注意复用Request对象或使用Cache API。 - 结合
itty-router-openapi或hono-openapi自动生成文档。 - 当需要 WebSocket/SSE,可引用
hono/ws或hono/sse。
缓存策略:
app.get('/health', async (c) => {
return c.text('ok', 200, {
'Cache-Control': 'max-age=30',
});
});
app.get('/articles', cacheMiddleware(), async (c) => {
// ...
});
在 Cloudflare Workers 中可用 c.executionCtx.waitUntil(cache.put(...)) 延迟写缓存。
安全最佳实践
使用
secureHeaders()设置Content-Security-Policy,Strict-Transport-Security,X-Frame-Options。JWT 验证使用
hono/jwt:app.use( '/api/*', jwt({ secret: c.env.JWT_SECRET, cookie: 'access_token', algorithm: 'HS256', }) );对外部输入全部走
zod/valibot校验,防止类型穿透。对于文件上传,结合 R2/S3 签名上传 + 临时 URL。
记录审计日志:可将请求信息写入 Workers Analytics Engine 或第三方服务。
测试策略
单元测试
import { describe, it, expect } from 'vitest';
import app from '../src/app';
describe('GET /health', () => {
it('should return ok', async () => {
const res = await app.request('/health');
expect(res.status).toBe(200);
expect(await res.text()).toBe('ok');
});
});
Hono 提供 app.request 直接在 Node 里模拟请求,无需启动服务器。
集成测试
- Cloudflare Workers:使用
@cloudflare/workers-types与miniflare。 - Node 环境:搭配
supertest。 - Edge Function:在部署前使用平台提供的本地模拟器(如
wrangler dev、vercel dev)。
部署与观察
- Cloudflare Workers:
wrangler deploy;配置 Durable Objects、KV、R2 于wrangler.toml。 - Vercel Edge:导出
export default app,在vercel.json指定runtime: "edge"。 - Node Server:使用
@hono/node-server或fastly-hono-adapter。 - Docker:将
app.fetch包装在server.listen中,注意 Node 端口。
监控与日志:
- Cloudflare Logs / Workers Analytics Engine。
- OpenTelemetry:可结合
hono/opentelemetry输出 trace。 - 使用平台原生指标(Vercel Speed Insights、Deno Deploy Metrics)。
常见陷阱
- Edge 环境没有 Node polyfills,避免依赖
fs,crypto.randomBytes等 Node 特有 API。 await中不要阻塞事件循环:使用Promise.all并行执行。- 注意
Request/Response流只能读取一次;需要复用时调用req.clone(). app.fire()必须在fetchhandler 中调用,在 Cloudflare 中通常是export default app.- 如果在中间件中抛异常,却返回 200,多半是
await next()忘写。
推荐资源
- Hono 官方文档
- Awesome Hono:社区收集的中间件、模板。
- Cloudflare Workers Docs
- Hono Examples 仓库
- Edge Runtime Compared:不同 Edge 运行时对比。
合理运用以上实践,可以让 Hono 成为构建高性能、易维护 API 的利器,无论是 SaaS 后端、BFF 还是边缘函数都能胜任。


