Hono (Japanese for "flame" 🔥) is a small, fast web framework built on Web Standards. Works on all JS runtimes.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello!'))
app.post('/api', async (c) => c.json({ ok: true }, 201))
export default app
// Responses
c.text(str, status?) // plain text
c.json(obj, status?) // JSON
c.html(str) // HTML string
c.body(stream, headers?) // raw body
// Requests
c.req.param('id') // path params (type-inferred)
c.req.query('name') // query params
c.req.valid('json') // validated data (with validator)
c.req.header('x-token') // request headers
c.req.cookie('session') // cookies
// Variables/state
c.set('key', value) // set variable (shared via middleware)
c.get('key') // get variable
c.var.foo // access middleware-set variable
// Other
c.notFound() // 404 (avoid in RPC)
c.redirect('/path', 302) // redirect
// Basic routes
app.get('/users/:id', (c) => c.json({ id: c.req.param('id') }))
// Regex routes
app.get('/posts/:id{.+}', (c) => { /* matches /posts/a/b/c */ })
// Route grouping
const api = new Hono()
api.get('/posts', ...)
api.get('/users', ...)
app.route('/api', api)
// HTTP methods
app.get|post|put|patch|delete|options|head
// Multiple paths
app.get(['/home', '/'], (c) => c.text('Home'))
# npm
npm create hono@latest my-app -- --template cloudflare-workers --pm npm --install
# bun
bun create hono@latest my-app
# Common templates: cloudflare-workers, bun, deno, vercel, aws-lambda, nodejs
npm create arg passing: npm requires --, others optional. --template selects template, --pm specifies package manager, --install auto-installs deps.
Recommended: Use app.route() for modular apps, not RoR-style Controllers (type inference breaks).
// authors.ts - separate Hono instance per module
const app = new Hono()
.get('/', (c) => c.json('list'))
.post('/', (c) => c.json('create', 201))
.get('/:id', (c) => c.json(c.req.param('id')))
export default app
export type AppType = typeof app
// index.ts - compose
import authors from './authors'
const app = new Hono()
app.route('/authors', authors)
export default app
RPC: Chain methods to export types:
const route = app.get('/', (c) => c.json({ ok: true }))
export type AppType = typeof route // or typeof app
// Built-in
import { cors, logger, etag } from 'hono/middleware'
app.use('*', cors(), logger(), etag())
// Custom
app.use('*', async (c, next) => {
c.set('requestId', crypto.randomUUID())
await next()
})
// Path-specific
app.use('/api/*', authenticate())
// Error handling
app.onError((err, c) => {
return c.json({ error: err.message }, 500)
})
Common built-in middleware: cors, jwt, bearer-auth, basic-auth, logger, etag, secure-headers, cache, compress, body-limit, pretty-json, timing, request-id, ip-restriction
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
app.post('/posts',
zValidator('json', z.object({ title: z.string(), body: z.string() })),
(c) => {
const { title, body } = c.req.valid('json')
return c.json({ ok: true }, 201)
}
)
export type AppType = typeof app // export for client
Validation targets: json, form, query, param, header, cookie
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('http://localhost:8787/')
// Request
const res = await client.posts.$post({
json: { title: 'Hello', body: 'Hono!' }
})
// Response
if (res.ok) {
const data = await res.json()
}
Don't use c.notFound() in RPC mode — use c.json(..., 404) instead, or type inference breaks.
Path params use param, queries use query, forms use form, JSON use json.
import { Hono } from 'hono'
import type { FC } from 'hono/jsx'
const app = new Hono()
const Layout: FC = (props) => (
<html><body>{props.children}</body></html>
)
app.get('/', (c) => c.html(<Layout><h1>Hello!</h1></Layout>))
tsconfig.json needs:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}
| Helper | Purpose |
|---|---|
| -------- | --------- |
html | Template literal HTML |
cookie | Cookie read/write |
jwt | JWT encode/decode |
ssg | Static site generation |
streaming | Streaming responses |
accepts | Content negotiation |
factory | Reusable handlers (preserves type inference) |
// Define types
type Env = { Variables: { user: User } }
// Use
const app = new Hono<{ Variables: { user: User } }>()
app.use('*', async (c, next) => {
c.set('user', { id: 1, name: 'Alice' })
await next()
})
app.get('/me', (c) => c.json(c.var.user))
// Method 1: app.request()
const res = await app.request('/api/posts?page=1', {
method: 'POST',
body: JSON.stringify({ title: 'Hello' }),
headers: { 'Content-Type': 'application/json' }
})
// Method 2: fetch style
const res = await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify({ title: 'Hello' })
})
const data = await res.json()
Detailed docs loaded on demand:
共 1 个版本