为什么是 Hono
JavaScript 后端框架众多——Express(老牌、生态最大)、Fastify(高性能)、Koa(Express 进化版)……Hono 的定位是:在任何 JavaScript 运行时上都能运行的超轻量框架。
- Hono 日语"炎"(ほのお),象征火焰般的速度。Yusuke Wada 创建于 2021 年,核心只有约 13KB(gzip 后),无任何外部依赖。
-
Web Standards
Hono 基于 Web 标准 API(
Request/Response/Headers),这些 API 在浏览器、Cloudflare Workers、Bun、Node.js 中都原生支持,是跨运行时的基础。 - Router 算法 Hono 使用 RegExpRouter(正则预编译)和 SmartRouter(自动选择最优算法),路由匹配速度极快。
与主流框架性能对比
| 框架 | req/s(Bun) | 包大小 | 运行时支持 |
|---|---|---|---|
| Hono | ~120,000 | 13KB | Bun/Node/CF/Deno/边缘 |
| Fastify | ~85,000 | ~65KB | Node.js |
| Express | ~42,000 | ~220KB | Node.js |
| Koa | ~60,000 | ~26KB | Node.js |
快速开始
# 安装 Hono
bun add hono
# 或 npm install hono
// src/index.ts — 最简示例(同时支持 Bun 和 Node.js)
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello Hono!'));
app.get('/hello/:name', (c) => {
const name = c.req.param('name');
return c.json({ message: `Hello, ${name}!` });
});
// Bun 运行
export default app;
// 然后 bun run src/index.ts
// Node.js 运行(需要适配器)
import { serve } from '@hono/node-server';
serve({ fetch: app.fetch, port: 3000 });
路由系统
基础路由
const app = new Hono();
// HTTP 方法
app.get('/users', getAllUsers);
app.post('/users', createUser);
app.put('/users/:id', updateUser);
app.delete('/users/:id', deleteUser);
app.patch('/users/:id', patchUser);
// 路径参数
app.get('/users/:id', (c) => {
const id = c.req.param('id'); // 单个参数
const { id: uid } = c.req.param(); // 所有参数
return c.json({ id });
});
// 查询参数
app.get('/search', (c) => {
const q = c.req.query('q') ?? '';
const page = Number(c.req.query('page') ?? '1');
return c.json({ q, page });
});
// 通配符
app.get('/static/*', serveStatic);
// 正则路由
app.get('/post/:id{[0-9]+}', (c) => { ... });
路由分组(Router)
import { Hono } from 'hono';
// 创建子路由
const api = new Hono().basePath('/api');
const v1 = new Hono();
const userRouter = new Hono();
userRouter.get('/', getAllUsers);
userRouter.get('/:id', getUser);
userRouter.post('/', createUser);
const postRouter = new Hono();
postRouter.get('/', getAllPosts);
postRouter.post('/', createPost);
// 组合路由
v1.route('/users', userRouter);
v1.route('/posts', postRouter);
api.route('/v1', v1);
const app = new Hono();
app.route('', api);
// 最终路由:GET /api/v1/users、POST /api/v1/posts 等
export default app;
中间件系统
Hono 的中间件与 Express/Koa 类似,但使用 Web 标准的 Request/Response API,并支持类型推断:
import { Hono } from 'hono';
import { logger } from 'hono/logger';
import { cors } from 'hono/cors';
import { bearerAuth } from 'hono/bearer-auth';
import { compress } from 'hono/compress';
import { timing } from 'hono/timing';
const app = new Hono();
// 内置中间件 — 全局应用
app.use(logger()); // 请求日志
app.use(timing()); // Server-Timing 头
app.use(compress()); // gzip 压缩
// CORS 配置
app.use(cors({
origin: ['https://myapp.com', 'http://localhost:5173'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
// 自定义中间件(带类型扩展)
type Env = {
Variables: {
userId: string;
user: { id: string; name: string };
};
};
const authMiddleware = createMiddleware<Env>(async (c, next) => {
const token = c.req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return c.json({ error: 'Unauthorized' }, 401);
}
// 验证 token,设置用户信息到上下文
c.set('userId', 'user-123');
await next(); // 调用下一个中间件/处理器
});
// 对特定路由应用中间件
app.use('/api/*', authMiddleware);
app.get('/api/profile', (c) => {
const userId = c.get('userId'); // 类型安全
return c.json({ userId });
});
请求与响应 API
// Context (c) 对象的常用 API
app.post('/demo', async (c) => {
// 读取请求
const body = await c.req.json(); // JSON body
const text = await c.req.text(); // 文本 body
const form = await c.req.formData(); // 表单数据
const blob = await c.req.blob(); // 二进制
const header = c.req.header('Content-Type');
const method = c.req.method;
const url = c.req.url;
const path = c.req.path;
// 发送响应
return c.json({ ok: true }); // JSON
return c.text('Hello'); // 纯文本
return c.html('<h1>Hi</h1>'); // HTML
return c.redirect('/new-url', 301); // 重定向
return c.body(buffer, { status: 200, // 自定义
headers: { 'Content-Type': 'image/png' }
});
// 流式响应(SSE/大文件)
return c.streamText(async (stream) => {
await stream.writeln('data: hello\n');
await stream.sleep(1000);
await stream.writeln('data: world\n');
});
});
请求验证(Zod Validator)
Hono 提供了与 Zod 深度集成的验证中间件,让输入验证和类型推断同步完成:
bun add zod @hono/zod-validator
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
// 定义 Schema
const createUserSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(['user', 'admin']).default('user'),
});
const userIdSchema = z.object({
id: z.string().uuid(),
});
const querySchema = z.object({
page: z.coerce.number().default(1),
limit: z.coerce.number().max(100).default(20),
search: z.string().optional(),
});
app.post(
'/users',
zValidator('json', createUserSchema), // 验证请求体
async (c) => {
const data = c.req.valid('json'); // 已验证,类型安全
// data.name: string
// data.email: string
// data.role: 'user' | 'admin'
return c.json({ id: 'new-id', ...data }, 201);
}
);
app.get(
'/users',
zValidator('query', querySchema), // 验证查询参数
async (c) => {
const { page, limit, search } = c.req.valid('query');
return c.json({ page, limit, search });
}
);
// 自定义验证错误响应
zValidator('json', createUserSchema, (result, c) => {
if (!result.success) {
return c.json({
error: 'Validation failed',
details: result.error.flatten(),
}, 400);
}
});
错误处理
import { HTTPException } from 'hono/http-exception';
// 抛出 HTTP 异常
app.get('/protected', (c) => {
throw new HTTPException(401, { message: '需要登录' });
});
// 全局错误处理器
app.onError((err, c) => {
if (err instanceof HTTPException) {
return err.getResponse();
}
console.error(err);
return c.json({ error: 'Internal Server Error' }, 500);
});
// 404 处理
app.notFound((c) => {
return c.json({ error: `路径 ${c.req.path} 不存在` }, 404);
});
OpenAPI / Swagger 集成
使用 @hono/zod-openapi 可以从 Zod Schema 自动生成 OpenAPI 文档:
bun add @hono/zod-openapi @hono/swagger-ui
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';
import { swaggerUI } from '@hono/swagger-ui';
const app = new OpenAPIHono();
const UserSchema = z.object({
id: z.string().openapi({ example: 'user-123' }),
name: z.string().openapi({ example: '张三' }),
}).openapi('User');
const getUserRoute = createRoute({
method: 'get',
path: '/users/{id}',
summary: '获取用户信息',
tags: ['Users'],
request: {
params: z.object({ id: z.string() }),
},
responses: {
200: {
content: { 'application/json': { schema: UserSchema } },
description: '成功返回用户',
},
404: { description: '用户不存在' },
},
});
app.openapi(getUserRoute, async (c) => {
const { id } = c.req.valid('param');
return c.json({ id, name: '张三' });
});
// 生成 OpenAPI JSON
app.doc('/openapi.json', {
openapi: '3.0.0',
info: { title: 'My API', version: '1.0.0' },
});
// Swagger UI 界面
app.get('/docs', swaggerUI({ url: '/openapi.json' }));
export default app;
Hono RPC:Hono 还提供了 hono/client,可以从服务端路由定义直接生成类型安全的客户端调用,类似轻量版 tRPC。对于不需要完整 tRPC 的项目是很好的选择。
完整 CRUD API 示例
// src/routes/posts.ts — 博客文章 CRUD
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
type Post = { id: string; title: string; content: string; createdAt: Date };
const posts = new Map<string, Post>();
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
});
export const postsRouter = new Hono()
.get('/', (c) => {
return c.json([...posts.values()]);
})
.get('/:id', (c) => {
const post = posts.get(c.req.param('id'));
if (!post) return c.json({ error: 'Not found' }, 404);
return c.json(post);
})
.post('/', zValidator('json', createPostSchema), async (c) => {
const data = c.req.valid('json');
const post: Post = { id: crypto.randomUUID(), ...data, createdAt: new Date() };
posts.set(post.id, post);
return c.json(post, 201);
})
.delete('/:id', (c) => {
const deleted = posts.delete(c.req.param('id'));
if (!deleted) return c.json({ error: 'Not found' }, 404);
return c.body(null, 204);
});