hono
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHono Skill
Hono 技能
Build Hono web applications. This skill provides inline API knowledge for AI. Use to test endpoints.
npx hono request构建Hono Web应用。本技能为AI提供嵌入式API知识。使用测试端点。
npx hono requestLatest Documentation
最新文档
For details beyond this inline reference, fetch the latest documentation from https://hono.dev. Get the index of doc pages from , then fetch a page with the header to receive it as Markdown:
https://hono.dev/llms.txtAccept: text/markdownbash
curl -H "Accept: text/markdown" https://hono.dev/docs/helpers/cookie如需了解本嵌入式参考之外的详细内容,请从https://hono.dev获取最新文档。从`https://hono.dev/llms.txt`获取文档页面索引,然后通过`Accept: text/markdown`请求头获取Markdown格式的页面:
bash
curl -H "Accept: text/markdown" https://hono.dev/docs/helpers/cookieHono CLI Usage
Hono CLI 使用方法
Request Testing
请求测试
Test endpoints without starting an HTTP server. Uses internally.
app.request()bash
undefined无需启动HTTP服务器即可测试端点。内部使用。
app.request()bash
undefinedGET request
GET 请求
npx hono request [file] -P /path
npx hono request [file] -P /path
POST request with JSON body
带JSON请求体的POST请求
npx hono request [file] -X POST -P /api/users -d '{"name": "test"}'
**Note:** Do not pass credentials directly in CLI arguments. Use environment variables for sensitive values. `hono request` does not support Cloudflare Workers bindings (KV, D1, R2, etc.). When bindings are required, use `workers-fetch` instead:
```bash
npx workers-fetch /path
npx workers-fetch -X POST -H "Content-Type:application/json" -d '{"name":"test"}' /api/usersnpx hono request [file] -X POST -P /api/users -d '{"name": "test"}'
**注意:** 不要在CLI参数中直接传递凭证。敏感值请使用环境变量。`hono request`不支持Cloudflare Workers绑定(KV、D1、R2等)。当需要绑定时,请改用`workers-fetch`:
```bash
npx workers-fetch /path
npx workers-fetch -X POST -H "Content-Type:application/json" -d '{"name":"test"}' /api/usersHono API Reference
Hono API 参考
App Constructor
应用构造函数
ts
import { Hono } from 'hono'
const app = new Hono()
// With TypeScript generics
type Env = {
Bindings: { DATABASE: D1Database; KV: KVNamespace }
Variables: { user: User }
}
const app = new Hono<Env>()ts
import { Hono } from 'hono'
const app = new Hono()
// 使用TypeScript泛型
type Env = {
Bindings: { DATABASE: D1Database; KV: KVNamespace }
Variables: { user: User }
}
const app = new Hono<Env>()Routing Methods
路由方法
ts
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) // all HTTP methods
app.on('PURGE', '/path', handler) // custom method
app.on(['PUT', 'DELETE'], '/path', handler) // multiple methodsts
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) // 多个方法Routing Patterns
路由模式
ts
// Path parameters
app.get('/user/:name', (c) => {
const name = c.req.param('name')
return c.json({ name })
})
// Multiple params
app.get('/posts/:id/comments/:commentId', (c) => {
const { id, commentId } = c.req.param()
})
// Optional parameters
app.get('/api/animal/:type?', (c) => c.text('Animal!'))
// Wildcards
app.get('/wild/*/card', (c) => c.text('Wildcard'))
// Regexp constraints
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
const { date, title } = c.req.param()
})
// Chained routes
app
.get('/endpoint', (c) => c.text('GET'))
.post((c) => c.text('POST'))
.delete((c) => c.text('DELETE'))ts
// 路径参数
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 Grouping
路由分组
ts
// Using route()
const api = new Hono()
api.get('/users', (c) => c.json([]))
const app = new Hono()
app.route('/api', api) // mounts at /api/users
// Using basePath()
const app = new Hono().basePath('/api')
app.get('/users', (c) => c.json([])) // GET /api/usersts
// 使用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/usersError Handling
错误处理
ts
app.notFound((c) => c.json({ message: 'Not Found' }, 404))
app.onError((err, c) => {
console.error(err)
return c.json({ message: 'Internal Server Error' }, 500)
})ts
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)
Response Methods
响应方法
ts
c.text('Hello') // text/plain
c.json({ message: 'Hello' }) // application/json
c.html('<h1>Hello</h1>') // text/html
c.redirect('/new-path') // 302 redirect
c.redirect('/new-path', 301) // 301 redirect
c.body('raw body', 200, headers) // raw response
c.notFound() // 404 responsets
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 响应Headers & Status
请求头与状态码
ts
c.status(201)
c.header('X-Custom', 'value')
c.header('Cache-Control', 'no-store')ts
c.status(201)
c.header('X-Custom', 'value')
c.header('Cache-Control', 'no-store')Variables (request-scoped data)
变量(请求作用域数据)
ts
// In middleware
c.set('user', { id: 1, name: 'Alice' })
// In handler
const user = c.get('user')
// or
const user = c.var.userts
// 在中间件中
c.set('user', { id: 1, name: 'Alice' })
// 在处理器中
const user = c.get('user')
// 或
const user = c.var.userEnvironment (Cloudflare Workers)
环境(Cloudflare Workers)
ts
const value = await c.env.KV.get('key')
const db = c.env.DATABASE
c.executionCtx.waitUntil(promise)ts
const value = await c.env.KV.get('key')
const db = c.env.DATABASE
c.executionCtx.waitUntil(promise)Renderer
渲染器
ts
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>))ts
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)
HonoRequest (c.req)
ts
c.req.param('id') // path parameter
c.req.param() // all path params as object
c.req.query('page') // query string parameter
c.req.query() // all query params as object
c.req.queries('tags') // multiple values: ?tags=A&tags=B → ['A', 'B']
c.req.header('Authorization') // request header
c.req.header() // all headers (keys are lowercase)
// Body parsing
await c.req.json() // parse JSON body
await c.req.text() // parse text body
await c.req.formData() // parse as FormData
await c.req.parseBody() // parse multipart/form-data or urlencoded
await c.req.arrayBuffer() // parse as ArrayBuffer
await c.req.blob() // parse as Blob
// Validated data (used with validator middleware)
c.req.valid('json')
c.req.valid('query')
c.req.valid('form')
c.req.valid('param')
// Properties
c.req.url // full URL string
c.req.path // pathname
c.req.method // HTTP method
c.req.raw // underlying Request objectts
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
// 验证后的数据(与validator中间件配合使用)
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对象Middleware
中间件
Using Built-in Middleware
使用内置中间件
ts
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 { jwk } from 'hono/jwk'
import { csrf } from 'hono/csrf'
import { ipRestriction } from 'hono/ip-restriction'
import { bodyLimit } from 'hono/body-limit'
import { timeout } from 'hono/timeout'
import { requestId } from 'hono/request-id'
import { methodOverride } from 'hono/method-override'
import { methodNotAllowed } from 'hono/method-not-allowed'
import { languageDetector } from 'hono/language'
import { some, every, except } from 'hono/combine'
import { contextStorage, getContext } from 'hono/context-storage'
import { trailingSlash, trimTrailingSlash } from 'hono/trailing-slash'
// Registration
app.use(logger()) // all routes
app.use('/api/*', cors()) // specific path
app.post('/api/*', basicAuth({ username: 'admin', password: 'secret' }))ts
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 { jwk } from 'hono/jwk'
import { csrf } from 'hono/csrf'
import { ipRestriction } from 'hono/ip-restriction'
import { bodyLimit } from 'hono/body-limit'
import { timeout } from 'hono/timeout'
import { requestId } from 'hono/request-id'
import { methodOverride } from 'hono/method-override'
import { methodNotAllowed } from 'hono/method-not-allowed'
import { languageDetector } from 'hono/language'
import { some, every, except } from 'hono/combine'
import { contextStorage, getContext } from 'hono/context-storage'
import { trailingSlash, trimTrailingSlash } from 'hono/trailing-slash'
// 注册
app.use(logger()) // 所有路由
app.use('/api/*', cors()) // 指定路径
app.post('/api/*', basicAuth({ username: 'admin', password: 'secret' }))Custom Middleware
自定义中间件
ts
// Inline
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`)
})
// Reusable with 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)ts
// 内联方式
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)Middleware Execution Order
中间件执行顺序
Middleware executes in registration order. calls the next middleware/handler, and code after runs on the way back:
await next()next()Request → mw1 before → mw2 before → handler → mw2 after → mw1 after → Responsets
app.use(async (c, next) => {
// before handler
await next()
// after handler
})中间件按注册顺序执行。调用下一个中间件/处理器,之后的代码在返回阶段运行:
await next()next()请求 → mw1 前置逻辑 → mw2 前置逻辑 → 处理器 → mw2 后置逻辑 → mw1 后置逻辑 → 响应ts
app.use(async (c, next) => {
// 处理器执行前的逻辑
await next()
// 处理器执行后的逻辑
})Validation
验证
Validation targets: , , , , , .
jsonformqueryheaderparamcookie验证目标:、、、、、。
jsonformqueryheaderparamcookieZod Validator
Zod 验证器
ts
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') // fully typed
return c.json(data, 201)
})ts
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 Validator
Valibot / 标准 Schema 验证器
ts
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)
})ts
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
JSX
Setup
配置
In :
tsconfig.jsonjson
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}Or use pragma:
/** @jsxImportSource hono/jsx */Important: Files using JSX must have a extension. Rename to or the compiler will fail.
.tsx.ts.tsx在中:
tsconfig.jsonjson
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}或使用编译指令:
/** @jsxImportSource hono/jsx */重要提示: 使用JSX的文件必须使用扩展名。将重命名为,否则编译会失败。
.tsx.ts.tsxComponents
组件
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>
)
})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 Middleware
jsxRenderer 中间件
Use middleware for layouts. For details, see https://hono.dev/docs/middleware/builtin/jsx-renderer
jsxRenderer使用中间件实现布局。详情请见https://hono.dev/docs/middleware/builtin/jsx-renderer
jsxRendererAsync Components
异步组件
tsx
const UserList = async () => {
const users = await fetchUsers()
return (
<ul>
{users.map((u) => (
<li>{u.name}</li>
))}
</ul>
)
}tsx
const UserList = async () => {
const users = await fetchUsers()
return (
<ul>
{users.map((u) => (
<li>{u.name}</li>
))}
</ul>
)
}Fragments
片段
tsx
const Items = () => (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
)tsx
const Items = () => (
<>
<li>Item 1</li>
<li>Item 2</li>
</>
)Streaming
流式传输
ts
import { stream, streamText, streamSSE } from 'hono/streaming'
// Basic stream
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)
})
})
// Text stream
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)
}
})
})ts
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')
})
})
// 服务器发送事件(SSE)
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)
}
})
})Testing with app.request()
使用app.request()进行测试
Test endpoints without starting an HTTP server:
ts
// GET
const res = await app.request('/posts')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ posts: [] })
// POST with JSON
const res = await app.request('/posts', {
method: 'POST',
body: JSON.stringify({ title: 'Hello' }),
headers: { 'Content-Type': 'application/json' }
})
// POST with FormData
const formData = new FormData()
formData.append('name', 'Alice')
const res = await app.request('/users', { method: 'POST', body: formData })
// With mock env (Cloudflare Workers bindings)
const res = await app.request('/api/data', {}, { KV: mockKV, DATABASE: mockDB })
// Using Request object
const req = new Request('http://localhost/api', { method: 'DELETE' })
const res = await app.request(req)无需启动HTTP服务器即可测试端点:
ts
// GET
const res = await app.request('/posts')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ posts: [] })
// 带JSON的POST请求
const res = await app.request('/posts', {
method: 'POST',
body: JSON.stringify({ title: 'Hello' }),
headers: { 'Content-Type': 'application/json' }
})
// 带FormData的POST请求
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)
Hono 客户端(RPC)
Type-safe API client using shared types between server and client.
IMPORTANT: Routes MUST be chained for type inference to work. Without chaining, the client cannot infer route types.
ts
// Server: routes MUST be chained to preserve types
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
// Client: use hc() with the exported type
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() // fully typedType utilities:
ts
import type { InferRequestType, InferResponseType } from 'hono/client'
type ReqType = InferRequestType<typeof client.posts.$post>
type ResType = InferResponseType<typeof client.posts.$post, 200>使用服务端与客户端共享类型的类型安全API客户端。
重要提示: 路由必须链式调用才能进行类型推断。不链式调用的话,客户端无法推断路由类型。
ts
// 服务端:路由必须链式调用以保留类型
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() // 完全类型化类型工具:
ts
import type { InferRequestType, InferResponseType } from 'hono/client'
type ReqType = InferRequestType<typeof client.posts.$post>
type ResType = InferResponseType<typeof client.posts.$post, 200>Helpers
工具函数
Helpers are utility functions imported from :
hono/<helper-name>ts
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' // or other adapterAvailable helpers: Accepts, Adapter, ConnInfo, Cookie, css, Dev, Factory, html, JWT, Proxy, Route, SSG, Streaming, Testing, WebSocket.
For details, see (fetch with ).
https://hono.dev/docs/helpers/<helper-name>Accept: text/markdown工具函数是从导入的实用函数:
hono/<helper-name>ts
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。
详情请见(通过请求获取)。
https://hono.dev/docs/helpers/<helper-name>Accept: text/markdownFactory
Factory
Use to define once and share it across app, middleware, and handlers:
createFactoryEnvts
import { createFactory } from 'hono/factory'
const factory = createFactory<Env>()
// Create app (Env type is inherited)
const app = factory.createApp()
// Create middleware (Env type is inherited, no need to pass generics)
const mw = factory.createMiddleware(async (c, next) => {
await next()
})
// Create handlers separately (preserves type inference)
const handlers = factory.createHandlers(logger(), (c) => c.json({ message: 'Hello' }))
app.get('/api', ...handlers)使用一次性定义,并在应用、中间件和处理器之间共享:
createFactoryEnvts
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)Best Practices
最佳实践
- Write handlers inline in route definitions for proper type inference of path params.
- Use to organize large apps by feature, not Rails-style controllers.
app.route() - Use to share Env type across app, middleware, and handlers.
createFactory() - Use /
c.set()to pass data between middleware and handlers.c.get() - Chain validators for multiple request parts (param + query + json).
- Export app type for RPC:
export type AppType = typeof routes - Use for testing — no server startup needed.
app.request()
- 在路由定义中内联编写处理器,以正确推断路径参数的类型。
- 使用按功能组织大型应用,而非Rails风格的控制器。
app.route() - 使用在应用、中间件和处理器之间共享Env类型。
createFactory() - 使用/
c.set()在中间件和处理器之间传递数据。c.get() - 为多个请求部分(参数+查询+json)链式调用验证器。
- 导出应用类型用于RPC:
export type AppType = typeof routes - 使用进行测试——无需启动服务器。
app.request()
Adapters
适配器
Hono runs on multiple runtimes. The default export works for Cloudflare Workers, Deno, and Bun. For Node.js, use the Node adapter:
ts
// Cloudflare Workers / Deno / Bun
export default app
// Node.js
import { serve } from '@hono/node-server'
serve(app)Hono可在多种运行时运行。默认导出适用于Cloudflare Workers、Deno和Bun。对于Node.js,请使用Node适配器:
ts
// Cloudflare Workers / Deno / Bun
export default app
// Node.js
import { serve } from '@hono/node-server'
serve(app)