Install
openclaw skills install honoHono web framework skill for building APIs across all JS runtimes (Cloudflare Workers, Deno, Bun, Node.js, etc.). Covers routing, middleware, JSX, RPC client, Zod validation, and context extensions.
openclaw skills install honoHono (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: