夏日幽灵 サマーゴースト (2021)
Hello, Hono honojs api Hono 后端
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-appcd my-hono-appnpm installnpm 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 还是边缘函数都能胜任。