Hono Framework Reference

Hono — ultrafast edge-first web framework for TypeScript. Works on Cloudflare Workers, Deno, Bun, Node.js, and Vercel Edge.

1. Routing

import { Hono } from 'hono'

const app = new Hono()

// Basic routes
app.get('/', (c) => c.text('Hello Hono!'))
app.post('/articles', createArticle)
app.put('/articles/:id', updateArticle)
app.delete('/articles/:id', deleteArticle)

// Path parameters
app.get('/articles/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id })
})

// Multiple params
app.get('/users/:userId/posts/:postId', (c) => {
  const { userId, postId } = c.req.param()
  return c.json({ userId, postId })
})

// Wildcard
app.get('/static/*', serveStatic({ root: './public' }))

// All methods
app.all('/any', (c) => c.text(`Method: ${c.req.method}`))

export default app

2. Route Groups & Chaining

import { Hono } from 'hono'
import { jwt } from 'hono/jwt'

const app = new Hono()

// Route group
const api = new Hono().basePath('/api/v1')

api.use('*', jwt({ secret: Bun.env.JWT_SECRET! }))

api.get('/articles', listArticles)
api.post('/articles', createArticle)

// Sub-app mounting
app.route('/api/v1', api)

// Method chaining on same path
app
  .get('/users', listUsers)
  .post('/users', createUser)

export default app

3. Middleware

import { Hono } from 'hono'
import { cors }   from 'hono/cors'
import { logger } from 'hono/logger'
import { prettyJSON } from 'hono/pretty-json'
import { secureHeaders } from 'hono/secure-headers'
import { rateLimiter } from 'hono-rate-limiter'

const app = new Hono()

// Built-in middleware
app.use('*', logger())
app.use('*', cors({
  origin: ['https://app.example.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowHeaders: ['Authorization', 'Content-Type'],
  credentials: true,
}))
app.use('*', secureHeaders())
app.use('/api/v1/*', prettyJSON())

// Custom middleware
app.use('*', async (c, next) => {
  const start = Date.now()
  await next()
  c.header('X-Response-Time', `${Date.now() - start}ms`)
})

// Middleware on specific routes
app.use('/admin/*', async (c, next) => {
  const payload = c.get('jwtPayload')
  if (payload?.role !== 'admin') return c.json({ error: 'Forbidden' }, 403)
  await next()
})

4. Validation with Zod

import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const createArticleSchema = z.object({
  title:   z.string().min(5).max(300),
  content: z.string().min(50),
  status:  z.enum(['draft', 'published']).default('draft'),
  tags:    z.array(z.string()).max(10).optional(),
})

app.post(
  '/articles',
  zValidator('json', createArticleSchema),
  async (c) => {
    const data = c.req.valid('json')  // fully typed!
    const article = await db.articles.create(data)
    return c.json(article, 201)
  }
)

// Query param validation
const searchSchema = z.object({
  q:     z.string().optional(),
  page:  z.coerce.number().min(1).default(1),
  limit: z.coerce.number().min(1).max(100).default(20),
})

app.get('/search', zValidator('query', searchSchema), (c) => {
  const { q, page, limit } = c.req.valid('query')
  return c.json({ q, page, limit })
})

5. RPC / hc Client

// server — define typed routes
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'

const articlesRoute = new Hono()
  .get('/', (c) => c.json({ articles: [] }))
  .post('/', zValidator('json', createSchema), async (c) => {
    const body = c.req.valid('json')
    return c.json({ id: '1', ...body }, 201)
  })
  .get('/:id', (c) => c.json({ id: c.req.param('id') }))

export type AppType = typeof articlesRoute
export default articlesRoute

// client — fully type-safe, no code gen needed
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('http://localhost:3000')

const res = await client.articles.$get()
const articles = await res.json()  // typed!

const created = await client.articles.$post({
  json: { title: 'Hello', content: 'World...', status: 'draft' },
})
const article = await created.json()  // typed!

6. Deployment Targets

PlatformEntry PointNotes
Cloudflare Workersexport default appDefault, fastest
Bunexport default { port, fetch: app.fetch }Native Bun server
DenoDeno.serve(app.fetch)Deno Deploy
Node.jsserve(app) from @hono/node-serverNode compatibility
Vercel Edgeexport const GET = handle(app)Vercel Edge Runtime
AWS Lambdahandle() from hono/aws-lambdaLambda@Edge