Hono:專為邊緣運算設計的極輕量 Web 框架
6 分鐘閱讀 1,000 字
Hono:專為邊緣運算設計的極輕量 Web 框架
在 Node.js 的 Web 框架世界,Express 統治了十多年。但隨著 Cloudflare Workers、Deno Deploy、Bun 等新執行環境的崛起,需要一個能在多個執行環境中運行、體積極小的框架。Hono(日文「炎」)應運而生。
Hono 的核心特點
- 極輕量:核心不到 14KB(壓縮後),零依賴
- 多執行環境:同一份程式碼可在 Cloudflare Workers、Node.js、Deno、Bun、AWS Lambda 運行
- TypeScript 優先:型別推斷極為完善
- 速度快:基準測試中通常是最快的 JS Web 框架之一
- 類 Express API:學習曲線低
安裝與基本設定
# 使用官方 CLI 建立專案
npm create hono@latest my-app
# 選擇執行環境(cloudflare-workers / nodejs / deno / bun)
# 或手動安裝
npm install honoHello World
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => {
return c.text('Hello, Hono!');
});
app.get('/json', (c) => {
return c.json({ message: 'Hello', timestamp: Date.now() });
});
export default app;在不同環境中啟動:
# Node.js
npm install @hono/node-serverimport { serve } from '@hono/node-server';
import app from './app';
serve({ fetch: app.fetch, port: 3000 }, () => {
console.log('Server running on http://localhost:3000');
});路由系統
import { Hono } from 'hono';
const app = new Hono();
// 基本路由
app.get('/users', listUsers);
app.post('/users', createUser);
app.get('/users/:id', getUser);
app.put('/users/:id', updateUser);
app.delete('/users/:id', deleteUser);
// 路由參數
app.get('/posts/:year/:month', (c) => {
const { year, month } = c.req.param();
return c.json({ year, month });
});
// 查詢字串
app.get('/search', (c) => {
const query = c.req.query('q');
const page = Number(c.req.query('page') ?? 1);
return c.json({ query, page });
});
// 路由群組
const api = new Hono().basePath('/api');
api.get('/health', (c) => c.json({ status: 'ok' }));
app.route('/', api);中介軟體(Middleware)
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { jwt } from 'hono/jwt';
import { prettyJSON } from 'hono/pretty-json';
const app = new Hono();
// 內建中介軟體
app.use('*', logger());
app.use('*', cors({
origin: ['https://myapp.com', 'http://localhost:5173'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE']
}));
app.use('/api/*', prettyJSON());
// JWT 認證
app.use('/protected/*', jwt({ secret: process.env.JWT_SECRET! }));
app.get('/protected/profile', (c) => {
const payload = c.get('jwtPayload');
return c.json({ user: payload });
});
// 自訂中介軟體
app.use('*', async (c, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
c.res.headers.set('X-Response-Time', `${duration}ms`);
});型別安全的路由
Hono 最強的特性之一:完整的型別推斷,包括路由參數、查詢字串、請求體和回應:
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150)
});
const app = new Hono();
app.post(
'/users',
zValidator('json', CreateUserSchema),
async (c) => {
// body 已完全型別化
const body = c.req.valid('json');
// body.name: string
// body.email: string
// body.age: number
const user = await db.createUser(body);
return c.json(user, 201);
}
);RPC 模式:型別安全的 API 呼叫
Hono 獨特的 RPC 功能,讓前端呼叫後端 API 時享有完整的型別支援:
// server.ts
const route = app
.get('/todos', async (c) => {
const todos = await getTodos();
return c.json({ todos });
})
.post('/todos',
zValidator('json', z.object({ title: z.string() })),
async (c) => {
const { title } = c.req.valid('json');
const todo = await createTodo(title);
return c.json(todo, 201);
}
);
export type AppType = typeof route;
// client.ts(前端)
import { hc } from 'hono/client';
import type { AppType } from './server';
const client = hc<AppType>('http://localhost:3000');
// 完全型別安全的 API 呼叫
const res = await client.todos.$get();
const { todos } = await res.json();
// todos 的型別從伺服器端自動推斷
await client.todos.$post({
json: { title: '學習 Hono' }
});在 Cloudflare Workers 部署
// src/index.ts
import { Hono } from 'hono';
type Bindings = {
DB: D1Database; // Cloudflare D1
KV: KVNamespace; // Cloudflare KV
BUCKET: R2Bucket; // Cloudflare R2
JWT_SECRET: string; // 環境變數
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/kv/:key', async (c) => {
const key = c.req.param('key');
const value = await c.env.KV.get(key);
if (!value) return c.notFound();
return c.text(value);
});
app.get('/db/users', async (c) => {
const { results } = await c.env.DB.prepare('SELECT * FROM users').all();
return c.json(results);
});
export default app;# 部署到 Cloudflare Workers
npx wrangler deploy錯誤處理
import { Hono, HTTPException } from 'hono';
const app = new Hono();
// 自訂錯誤處理
app.onError((err, c) => {
if (err instanceof HTTPException) {
return c.json({ error: err.message }, err.status);
}
console.error('Unexpected error:', err);
return c.json({ error: 'Internal Server Error' }, 500);
});
// 404 處理
app.notFound((c) => {
return c.json({ error: `路由 ${c.req.path} 不存在` }, 404);
});
// 在路由中拋出 HTTP 異常
app.get('/users/:id', async (c) => {
const user = await findUser(c.req.param('id'));
if (!user) {
throw new HTTPException(404, { message: '使用者不存在' });
}
return c.json(user);
});與 Express 的比較
| 特性 | Hono | Express |
|---|---|---|
| 體積 | ~14KB | ~200KB+ |
| TypeScript | 原生 | 需 @types |
| 多執行環境 | 是 | Node.js only |
| 型別安全路由 | 是 | 否 |
| RPC 客戶端 | 是 | 否 |
| 生態系 | 成長中 | 非常豐富 |
小結
Hono 不是要取代 Express,而是為新的執行環境生態而生。如果你在開發 Cloudflare Workers 應用、需要在多個執行環境部署同一份程式碼、或者重視 TypeScript 的型別安全,Hono 是目前最佳的選擇。它的 RPC 模式更是讓全端開發者眼前一亮——前後端型別共用,告別手動維護 API 型別定義的痛苦。
分享這篇文章