关于 hono
这项技能指导使用 Hono 构建 API 和服务端应用,涵盖路由、中间件、验证、类型安全与测试,并介绍多个运行时的设置。
主要功能
- 01跨运行时设置 Hono
- 02HTTP 路由与路由分组
- 03中间件与错误处理
- 04使用 Zod 验证请求
- 05类型化上下文与 RPC
- 06进程内 API 测试
适用场景
- 01搭建 Hono API
- 02使用 Zod 验证路由输入
- 03组织不同版本的 API 路由
- 04无需启动服务端测试端点
MCP 相关
该技能建议通过 Context7 MCP 先调用 `resolve-library-id`,再调用 `query-docs`,获取 Hono API 参考和运行时设置示例。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dralgorhythm/ claude-agentic-framework --skill hono安装后在 Claude Code 中输入/hono确认已加载。
详细介绍
Hono
如需完整的 API 参考、所有内置中间件以及更多运行时设置示例,请使用 Context7 MCP(
resolve-library-id,然后使用query-docs)。
概览
轻量、快速的 Web 框架,适用于 API 和服务端应用。Hono 4.x 可通过统一的 API 在 Node.js、Bun、Deno 和 Cloudflare Workers 上运行。
安装:pnpm add hono
工作流
创建基础 API:
- 创建 Hono 应用实例:
const app = new Hono() - 使用 HTTP 方法定义路由
- 添加中间件(CORS、日志记录、错误处理)
- 导出应用以供运行时适配器使用
- 使用 curl 或 Postman 测试端点
添加验证:
- 安装 Zod:
pnpm add zod @hono/zod-validator - 使用 Zod 定义架构
- 在路由中应用
zValidator中间件 - 处理验证错误
- 通过
c.req.valid()访问类型安全的请求数据
运行时设置
// Node.js
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
const app = new Hono();
serve({ fetch: app.fetch, port: 3000 });
// Bun
export default { port: 3000, fetch: app.fetch };
// Cloudflare Workers
export default app;
路由
// HTTP methods
app.get('/users', (c) => c.json({ users: [] }));
app.post('/users', (c) => c.json({ created: true }, 201));
app.put('/users/:id', (c) => c.json({ updated: true }));
app.delete('/users/:id', (c) => c.json({ deleted: true }));
app.on(['GET', 'POST'], '/multi', (c) => c.text(c.req.method));
// Path parameters
app.get('/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ userId: id });
});
// Multiple params
app.get('/posts/:postId/comments/:commentId', (c) => {
const { postId, commentId } = c.req.param();
return c.json({ postId, commentId });
});
// Wildcard
app.get('/files/*', (c) => c.text('File handler'));
// Regex constraint (only numeric IDs)
app.get('/posts/:id{[0-9]+}', (c) => c.json({ id: c.req.param('id') }));
路由组
const v1 = new Hono();
v1.get('/users', (c) => c.json({ version: 1, users: [] }));
const v2 = new Hono();
v2.get('/users', (c) => c.json({ version: 2, users: [] }));
app.route('/api/v1', v1);
app.route('/api/v2', v2);
请求处理
// Query params
app.get('/search', (c) => {
const q = c.req.query('q');
const page = c.req.query('page') ?? '1';
return c.json({ q, page });
});
// JSON body
app.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ received: body });
});
// Headers
app.get('/me', (c) => {
const auth = c.req.header('Authorization');
c.header('X-Custom-Header', 'value');
return c.json({ auth });
});
响应类型
c.json({ data: 'value' }) // JSON (default 200)
c.json({ created: true }, 201) // JSON with status
c.text('OK') // Plain text
c.html('<h1>Hello</h1>') // HTML
c.redirect('/new') // 302 redirect
c.redirect('https://example.com', 301)
c.notFound() // 404
中间件
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { secureHeaders } from 'hono/secure-headers';
import { prettyJSON } from 'hono/pretty-json';
app.use('*', logger());
app.use('*', secureHeaders());
app.use('*', cors({
origin: ['http://localhost:3000', 'https://example.com'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
if (process.env.NODE_ENV === 'development') {
app.use('*', prettyJSON());
}
自定义中间件
// Auth middleware — store data in context
const authMiddleware = async (c, next) => {
const token = c.req.header('Authorization');
if (!token) return c.json({ error: 'Unauthorized' }, 401);
c.set('user', { id: 1, name: 'Alice' }); // stored in context
await next();
};
app.use('/api/*', authMiddleware);
app.get('/api/profile', (c) => {
const user = c.get('user');
return c.json({ user });
});
使用 Zod 进行验证
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
const userSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(120).optional(),
});
// Validate body
app.post('/users', zValidator('json', userSchema), async (c) => {
const user = c.req.valid('json'); // fully typed
return c.json({ created: true, user }, 201);
});
// Validate path params
app.get('/users/:id', zValidator('param', z.object({ id: z.string().regex(/^\d+$/) })), (c) => {
const { id } = c.req.valid('param');
return c.json({ userId: id });
});
// Custom validation error response
app.post('/users', zValidator('json', userSchema, (result, c) => {
if (!result.success) {
return c.json({ error: 'Validation failed', details: result.error.flatten() }, 400);
}
}), handler);
错误处理
import { HTTPException } from 'hono/http-exception';
// Global error handler
app.onError((err, c) => {
if (err instanceof HTTPException) {
return c.json({ error: err.message, status: err.status }, err.status);
}
return c.json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'development' ? err.message : undefined,
}, 500);
});
// 404 handler
app.notFound((c) => c.json({ error: 'Not Found', path: c.req.path }, 404));
// Throw HTTP exceptions from route handlers
app.get('/protected', (c) => {
throw new HTTPException(403, { message: 'Forbidden' });
});
类型安全
类型化上下文变量
type Env = {
Variables: { user: { id: number; name: string } };
};
const app = new Hono<Env>();
app.use('/api/*', async (c, next) => {
c.set('user', { id: 1, name: 'Alice' }); // type-checked
await next();
});
app.get('/api/profile', (c) => {
const user = c.get('user'); // fully typed
return c.json({ user });
});
RPC 类型安全(Hono 客户端)
// server.ts — export the app type
const app = new Hono()
.get('/posts', (c) => c.json({ posts: [] }))
.post('/posts', async (c) => c.json({ created: true }, 201));
export type AppType = typeof app;
// client.ts — fully typed calls, no separate OpenAPI spec needed
import { hc } from 'hono/client';
import type { AppType } from './server';
const client = hc<AppType>('http://localhost:3000');
const res = await client.posts.$get();
const data = await res.json(); // { posts: [] }
测试
import { describe, it, expect } from 'vitest';
describe('API', () => {
const app = new Hono();
app.get('/hello', (c) => c.json({ message: 'Hello' }));
it('returns hello', async () => {
const res = await app.request('/hello');
expect(res.status).toBe(200);
expect(await res.json()).toEqual({ message: 'Hello' });
});
it('handles POST', async () => {
app.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ created: true, user: body }, 201);
});
const res = await app.request('/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice' }),
});
expect(res.status).toBe(201);
});
});
最佳实践
- 使用路由组将相关端点组织到模块化路由器中
- 使用 Zod 验证所有输入,确保类型安全并进行运行时验证
- 谨慎应用中间件——仅在每个路由组中使用所需的中间件
- 设置明确的 CORS 策略以用于生产环境——切勿在生产环境中使用宽松的 CORS
- 对中间件中设置的变量使用类型化上下文(
Hono<Env>) - 使用
app.onError()全局处理错误,以确保错误响应一致 - 使用
HTTPException,而不是手动构造错误响应 - 使用
app.request()进行测试——Hono 内置的测试工具(无需服务器) - 利用 RPC 类型实现类型安全的客户端-服务端通信
反模式
- ❌ 在路由之后应用日志记录中间件(这些路由不会被记录)
- ❌ 忘记在中间件中
await next()(会中断中间件链) - ❌ 仅在特定路由上使用
cors()(预检请求需要全局 CORS) - ❌ 多次解析请求正文(首次解析后应缓存)
- ❌ 不验证路径参数(始终验证用户输入)
- ❌ 使用
any类型,而不是适当的 Hono 泛型 - ❌ 在 CORS 配置中硬编码来源(请使用环境变量)
- ❌ 缺少错误处理器(会导致未处理的 Promise 拒绝)
- ❌ 忘记为运行时适配器导出应用
反馈循环
测试端点:
curl -X GET http://localhost:3000/api/users
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"[email protected]"}'
验证测试:
# Should return 400 with validation details
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"","email":"invalid"}'
性能测试:
pnpm add -D autocannon
npx autocannon -c 100 -d 10 http://localhost:3000/api/users
# Target: <10ms p99 latency for simple endpoints
评论
支持 Markdown