在建立 Hono 網頁應用程式,或使用者詢問 Hono API、路由、中介軟體、JSX、驗證、測試或串流時使用。當程式碼從 'hono' 或 'hono/*' 匯入,或使用者提及 Hono 時觸發。使用 `npx hono request` 測試端點。
Hono 技能
建立 Hono 網頁應用程式。此技能為 AI 提供內嵌 API 知識。使用 npx hono request 測試端點。如果已設定 hono-docs MCP 伺服器,請優先使用其工具取得最新文件,而非內嵌參考。
Hono CLI 使用方式
請求測試
無需啟動 HTTP 伺服器即可測試端點。內部使用 app.request()。
# GET 請求
npx hono request [file] -P /path
# POST 請求,含 JSON 主體
npx hono request [file] -X POST -P /api/users -d '{"name": "test"}'
注意: 請勿直接在 CLI 參數中傳遞憑證。敏感值請使用環境變數。hono request 不支援 Cloudflare Workers 繫結(KV、D1、R2 等)。當需要繫結時,請改用 workers-fetch:
npx workers-fetch /path
npx workers-fetch -X POST -H "Content-Type:application/json" -d '{"name":"test"}' /api/users
Hono API 參考
App 建構子
import { Hono } from 'hono'
const app = new Hono()
// 搭配 TypeScript 泛型
type Env = {
Bindings: { DATABASE: D1Database; KV: KVNamespace }
Variables: { user: User }
}
const app = new Hono<Env>()
路由方法
app.get('/path', handler)
app.post('/path', handler)
app.put('/path', handler)
app.delete('/path', handler)
app.patch('/path', handler)
app.options('/path', handler)
app.all('/path', handler) // 所有 HTTP 方法
app.on('PURGE', '/path', handler) // 自訂方法
app.on(['PUT', 'DELETE'], '/path', handler) // 多種方法
路由模式
// 路徑參數
app.get('/user/:name', (c) => {
const name = c.req.param('name')
return c.json({ name })
})
// 多個參數
app.get('/posts/:id/comments/:commentId', (c) => {
const { id, commentId } = c.req.param()
})
// 可選參數
app.get('/api/animal/:type?', (c) => c.text('Animal!'))
// 萬用字元
app.get('/wild/*/card', (c) => c.text('Wildcard'))
// 正規表達式限制
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
const { date, title } = c.req.param()
})
// 鏈式路由
app
.get('/endpoint', (c) => c.text('GET'))
.post((c) => c.text('POST'))
.delete((c) => c.text('DELETE'))
路由分組
// 使用 route()
const api = new Hono()
api.get('/users', (c) => c.json([]))
const app = new Hono()
app.route('/api', api) // 掛載在 /api/users
// 使用 basePath()
const app = new Hono().basePath('/api')
app.get('/users', (c) => c.json([])) // GET /api/users
錯誤處理
app.notFound((c) => c.json({ message: 'Not Found' }, 404))
app.onError((err, c) => {
console.error(err)
return c.json({ message: 'Internal Server Error' }, 500)
})
Context (c)
回應方法
c.text('Hello') // text/plain
c.json({ message: 'Hello' }) // application/json
c.html('<h1>Hello</h1>') // text/html
c.redirect('/new-path') // 302 重新導向
c.redirect('/new-path', 301) // 301 重新導向
c.body('raw body', 200, headers) // 原始回應
c.notFound() // 404 回應
標頭與狀態
c.status(201)
c.header('X-Custom', 'value')
c.header('Cache-Control', 'no-store')
變數(請求範圍資料)
// 在中介軟體中
c.set('user', { id: 1, name: 'Alice' })
// 在處理器中
const user = c.get('user')
// 或
const user = c.var.user
環境(Cloudflare Workers)
const value = await c.env.KV.get('key')
const db = c.env.DATABASE
c.executionCtx.waitUntil(promise)
渲染器
app.use(async (c, next) => {
c.setRenderer((content) =>
c.html(
<html><body>{content}</body></html>
)
)
await next()
})
app.get('/', (c) => c.render(<h1>Hello</h1>))
HonoRequest (c.req)
c.req.param('id') // 路徑參數
c.req.param() // 所有路徑參數(物件形式)
c.req.query('page') // 查詢字串參數
c.req.query() // 所有查詢參數(物件形式)
c.req.queries('tags') // 多值:?tags=A&tags=B → ['A', 'B']
c.req.header('Authorization') // 請求標頭
c.req.header() // 所有標頭(鍵值為小寫)
// 主體解析
await c.req.json() // 解析 JSON 主體
await c.req.text() // 解析文字主體
await c.req.formData() // 解析為 FormData
await c.req.parseBody() // 解析 multipart/form-data 或 urlencoded
await c.req.arrayBuffer() // 解析為 ArrayBuffer
await c.req.blob() // 解析為 Blob
// 驗證資料(搭配驗證中介軟體使用)
c.req.valid('json')
c.req.valid('query')
c.req.valid('form')
c.req.valid('param')
// 屬性
c.req.url // 完整 URL 字串
c.req.path // 路徑名稱
c.req.method // HTTP 方法
c.req.raw // 底層 Request 物件
中介軟體
使用內建中介軟體
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { basicAuth } from 'hono/basic-auth'
import { prettyJSON } from 'hono/pretty-json'
import { secureHeaders } from 'hono/secure-headers'
import { etag } from 'hono/etag'
import { compress } from 'hono/compress'
import { poweredBy } from 'hono/powered-by'
import { timing } from 'hono/timing'
import { cache } from 'hono/cache'
import { bearerAuth } from 'hono/bearer-auth'
import { jwt } from 'hono/jwt'
import { csrf } from 'hono/csrf'
import { ipRestriction } from 'hono/ip-restriction'
import { bodyLimit } from 'hono/body-limit'
import { requestId } from 'hono/request-id'
import { methodOverride } from 'hono/method-override'
import { trailingSlash, trimTrailingSlash } from 'hono/trailing-slash'
// 註冊
app.use(logger()) // 所有路由
app.use('/api/*', cors()) // 特定路徑
app.post('/api/*', basicAuth({ username: 'admin', password: 'secret' }))
自訂中介軟體
// 內聯
app.use(async (c, next) => {
const start = Date.now()
await next()
const elapsed = Date.now() - start
c.res.headers.set('X-Response-Time', `${elapsed}ms`)
})
// 使用 createMiddleware 可重複使用
import { createMiddleware } from 'hono/factory'
const auth = createMiddleware(async (c, next) => {
const token = c.req.header('Authorization')
if (!token) return c.json({ error: 'Unauthorized' }, 401)
await next()
})
app.use('/api/*', auth)
中介軟體執行順序
中介軟體依註冊順序執行。await next() 會呼叫下一個中介軟體/處理器,next() 之後的程式碼則在返回時執行:
Request → mw1 before → mw2 before → handler → mw2 after → mw1 after → Response
app.use(async (c, next) => {
// 處理器之前
await next()
// 處理器之後
})
驗證
驗證目標:json、form、query、header、param、cookie。
Zod 驗證器
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const schema = z.object({
title: z.string().min(1),
body: z.string()
})
app.post('/posts', zValidator('json', schema), (c) => {
const data = c.req.valid('json') // 完整型別
return c.json(data, 201)
})
Valibot / Standard Schema 驗證器
import { sValidator } from '@hono/standard-validator'
import * as v from 'valibot'
const schema = v.object({ name: v.string(), age: v.number() })
app.post('/users', sValidator('json', schema), (c) => {
const data = c.req.valid('json')
return c.json(data, 201)
})
JSX
設定
在 tsconfig.json 中:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}
或使用 pragma:/** @jsxImportSource hono/jsx */
重要: 使用 JSX 的檔案必須有 .tsx 副檔名。將 .ts 重新命名為 .tsx,否則編譯器會失敗。
元件
import type { PropsWithChildren } from 'hono/jsx'
const Layout = (props: PropsWithChildren) => (
<html>
<head>
<title>My App</title>
</head>
<body>{props.children}</body>
</html>
)
const UserCard = ({ name }: { name: string }) => (
<div class="card">
<h2>{name}</h2>
</div>
)
app.get('/', (c) => {
return c.html(
<Layout>
<UserCard name="Alice" />
</Layout>
)
})
jsxRenderer 中介軟體
使用 jsxRenderer 中介軟體處理版面配置。詳情請見 npx hono docs /docs/middleware/builtin/jsx-renderer。
非同步元件
const UserList = async () => {
const users = await fetchUsers()
return (
<ul>
{users.map((u) => (
<li>{u.name}</li>
))}
</ul>
)
}
片段
const Items = () => (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
)
串流
import { stream, streamText, streamSSE } from 'hono/streaming'
// 基本串流
app.get('/stream', (c) => {
return stream(c, async (stream) => {
stream.onAbort(() => console.log('Aborted'))
await stream.write(new Uint8Array([0x48, 0x65]))
await stream.pipe(readableStream)
})
})
// 文字串流
app.get('/stream-text', (c) => {
return streamText(c, async (stream) => {
await stream.writeln('Hello')
await stream.sleep(1000)
await stream.write('World')
})
})
// Server-Sent Events
app.get('/sse', (c) => {
return streamSSE(c, async (stream) => {
let id = 0
while (true) {
await stream.writeSSE({
data: JSON.stringify({ time: new Date().toISOString() }),
event: 'time-update',
id: String(id++)
})
await stream.sleep(1000)
}
})
})
使用 app.request() 測試
無需啟動 HTTP 伺服器即可測試端點:
// GET
const res = await app.request('/posts')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ posts: [] })
// POST 含 JSON
const res = await app.request('/posts', {
method: 'POST',
body: JSON.stringify({ title: 'Hello' }),
headers: { 'Content-Type': 'application/json' }
})
// POST 含 FormData
const formData = new FormData()
formData.append('name', 'Alice')
const res = await app.request('/users', { method: 'POST', body: formData })
// 搭配模擬環境(Cloudflare Workers 繫結)
const res = await app.request('/api/data', {}, { KV: mockKV, DATABASE: mockDB })
// 使用 Request 物件
const req = new Request('http://localhost/api', { method: 'DELETE' })
const res = await app.request(req)
Hono Client (RPC)
型別安全的 API 客戶端,在伺服器與客戶端之間共用型別。
重要:路由必須鏈式呼叫才能進行型別推論。若未鏈式呼叫,客戶端無法推論路由型別。
// 伺服器:路由必須鏈式呼叫以保留型別
const route = app
.post('/posts', zValidator('json', schema), (c) => {
return c.json({ ok: true }, 201)
})
.get('/posts', (c) => {
return c.json({ posts: [] })
})
export type AppType = typeof route
// 客戶端:使用 hc() 搭配匯出的型別
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('http://localhost:8787/')
const res = await client.posts.$post({ json: { title: 'Hello' } })
const data = await res.json() // 完整型別
型別工具:
import type { InferRequestType, InferResponseType } from 'hono/client'
type ReqType = InferRequestType<typeof client.posts.$post>
type ResType = InferResponseType<typeof client.posts.$post, 200>
輔助工具
輔助工具是從 hono/<helper-name> 匯入的實用函式:
import { getConnInfo } from 'hono/conninfo'
import { getCookie, setCookie, deleteCookie } from 'hono/cookie'
import { css, Style } from 'hono/css'
import { createFactory } from 'hono/factory'
import { html, raw } from 'hono/html'
import { stream, streamText, streamSSE } from 'hono/streaming'
import { testClient } from 'hono/testing'
import { upgradeWebSocket } from 'hono/cloudflare-workers' // 或其他適配器
可用的輔助工具:Accepts、Adapter、ConnInfo、Cookie、css、Dev、Factory、html、JWT、Proxy、Route、SSG、Streaming、Testing、WebSocket。
詳情請使用 npx hono docs /docs/helpers/<helper-name>。
Factory
使用 createFactory 定義一次 Env,並在應用程式、中介軟體和處理器之間共用:
import { createFactory } from 'hono/factory'
const factory = createFactory<Env>()
// 建立應用程式(Env 型別會繼承)
const app = factory.createApp()
// 建立中介軟體(Env 型別會繼承,無需傳遞泛型)
const mw = factory.createMiddleware(async (c, next) => {
await next()
})
// 分別建立處理器(保留型別推論)
const handlers = factory.createHandlers(logger(), (c) => c.json({ message: 'Hello' }))
app.get('/api', ...handlers)
最佳實踐
- 在路由定義中內聯撰寫處理器,以正確推論路徑參數的型別。
- 使用
app.route()按功能組織大型應用程式,而非 Rails 風格的控制器。 - 使用
createFactory()在應用程式、中介軟體和處理器之間共用 Env 型別。 - 使用
c.set()/c.get()在中介軟體和處理器之間傳遞資料。 - 鏈式使用驗證器處理多個請求部分(param + query + json)。
- 匯出應用程式型別供 RPC 使用:
export type AppType = typeof routes - 使用
app.request()進行測試 — 無需啟動伺服器。
適配器
Hono 可在多種執行環境中執行。預設匯出適用於 Cloudflare Workers、Deno 和 Bun。若使用 Node.js,請使用 Node 適配器:
// Cloudflare Workers / Deno / Bun
export default app
// Node.js
import { serve } from '@hono/node-server'
serve(app)






