Hello, Hono honojs api Hono 后端
1204 字
6 分钟
Hello, Hono
2025-05-24

为什么选择 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:

Terminal window
npm create hono@latest my-hono-app
cd my-hono-app
npm install
npm run dev

生成的项目默认使用 TypeScript 与 ESLint,可根据运行时切换 adapter

  • cloudflare-workers
  • bun
  • deno
  • node-server(Node.js 运行,配合 Fastly Compute、Lambda 等)

配置建议:

  • tsconfig.json 开启 strict, noUncheckedIndexedAccess
  • 配置 eslint-config-honotypescript-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-openapihono-openapi 自动生成文档。
  • 当需要 WebSocket/SSE,可引用 hono/wshono/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-typesminiflare
  • Node 环境:搭配 supertest
  • Edge Function:在部署前使用平台提供的本地模拟器(如 wrangler devvercel dev)。

部署与观察#

  • Cloudflare Workerswrangler deploy;配置 Durable Objects、KV、R2 于 wrangler.toml
  • Vercel Edge:导出 export default app,在 vercel.json 指定 runtime: "edge"
  • Node Server:使用 @hono/node-serverfastly-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() 必须在 fetch handler 中调用,在 Cloudflare 中通常是 export default app.
  • 如果在中间件中抛异常,却返回 200,多半是 await next() 忘写。

推荐资源#

合理运用以上实践,可以让 Hono 成为构建高性能、易维护 API 的利器,无论是 SaaS 后端、BFF 还是边缘函数都能胜任。

Hello, Hono
https://bangwu.top/posts/honojs/
作者
棒无
发布于
2025-05-24
许可协议
CC BY-NC-SA 4.0