hono-guide

Hono is a small, simple, and ultrafast web framework built on Web Standards. It works on any JavaScript runtime: Cloudflare Workers, Bun, Deno, Node.js, AWS Lambda, Vercel, Netlify, Fastly Compute, and more. The same code runs on all platforms.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "hono-guide" with this command: npx skills add vcode-sh/vibe-tools/vcode-sh-vibe-tools-hono-guide

Hono Guide

Hono is a small, simple, and ultrafast web framework built on Web Standards. It works on any JavaScript runtime: Cloudflare Workers, Bun, Deno, Node.js, AWS Lambda, Vercel, Netlify, Fastly Compute, and more. The same code runs on all platforms.

Quick Start

npm create hono@latest my-app

import { Hono } from 'hono' const app = new Hono()

app.get('/', (c) => c.text('Hello Hono!'))

export default app

Core Concepts

Routing

// HTTP methods app.get('/posts', (c) => c.json({ posts })) app.post('/posts', (c) => c.json({ message: 'Created' }, 201)) app.put('/posts/:id', (c) => c.json({ message: 'Updated' })) app.delete('/posts/:id', (c) => c.json({ message: 'Deleted' }))

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

// Optional params app.get('/api/animal/:type?', (c) => c.text('Animal!'))

// Wildcard app.get('/wild/*/card', (c) => c.text('Matched!'))

// Regexp app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => { const { date, title } = c.req.param() return c.json({ date, title }) })

// Chained routes app.get('/endpoint', (c) => c.text('GET')) .post((c) => c.text('POST')) .delete((c) => c.text('DELETE'))

Context API (c)

The Context object provides all request/response methods:

// Response methods 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.notFound() // 404 c.body(data, 200, headers) // raw response

// Status & headers c.status(201) c.header('X-Custom', 'value')

// Request data c.req.param('id') // path param c.req.query('q') // query string c.req.queries('tags') // multiple values c.req.header('Authorization') // header const body = await c.req.json() // JSON body const form = await c.req.parseBody() // form data

// Variables (pass data between middleware and handlers) c.set('user', userObj) const user = c.get('user') // or: c.var.user

// Environment (Cloudflare bindings, env vars) c.env.MY_KV // KV namespace c.env.DATABASE_URL // env variable

Middleware

Middleware runs before/after handlers in onion-layer order:

import { logger } from 'hono/logger' import { cors } from 'hono/cors' import { basicAuth } from 'hono/basic-auth'

// Apply to all routes app.use(logger()) app.use(cors())

// Apply to specific paths app.use('/api/', cors({ origin: 'https://example.com' })) app.use('/admin/', basicAuth({ username: 'admin', password: 'secret' }))

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

Execution order: middleware 1 start -> middleware 2 start -> handler -> middleware 2 end -> middleware 1 end

Built-in Middleware (import from hono/<name> )

Middleware Import Purpose

basicAuth

hono/basic-auth

HTTP Basic authentication

bearerAuth

hono/bearer-auth

Bearer token authentication

jwt

hono/jwt

JWT authentication

cors

hono/cors

CORS headers

csrf

hono/csrf

CSRF protection

logger

hono/logger

Request logging

secureHeaders

hono/secure-headers

Security headers (Helmet-like)

etag

hono/etag

ETag caching

cache

hono/cache

Cache API (CF Workers, Deno)

compress

hono/compress

Response compression

bodyLimit

hono/body-limit

Request body size limit

timeout

hono/timeout

Request timeout

prettyJSON

hono/pretty-json

Pretty-print JSON with ?pretty

requestId

hono/request-id

Unique request ID per request

ipRestriction

hono/ip-restriction

IP allow/deny lists

languageDetector

hono/language

i18n language detection

jsxRenderer

hono/jsx-renderer

JSX layout renderer

contextStorage

hono/context-storage

AsyncLocalStorage for Context

methodOverride

hono/method-override

HTTP method override

timing

hono/timing

Server-Timing header

Helpers (import from hono/<name> )

Helper Import Purpose

Cookie hono/cookie

get/set/delete cookies

JWT hono/jwt

sign/verify/decode JWT

Streaming hono/streaming

stream, streamText, streamSSE

WebSocket Platform-specific upgradeWebSocket handler

HTML hono/html

html template literals

CSS hono/css

CSS-in-JS(X)

Factory hono/factory

createMiddleware, createHandlers

Testing hono/testing

testClient for typed testing

Proxy hono/proxy

Reverse proxy helper

SSG hono/ssg

Static site generation

Accepts hono/accepts

Content negotiation (Accept-*)

Adapter hono/adapter

env(), getRuntimeKey()

ConnInfo Platform-specific Client remote address, connection info

Dev hono/dev

showRoutes(), getRouterName()

Route hono/route

matchedRoutes(), routePath()

Larger Applications

Use app.route() to split into sub-apps:

// authors.ts const authors = new Hono() .get('/', (c) => c.json('list authors')) .post('/', (c) => c.json('create author', 201)) .get('/:id', (c) => c.json(get ${c.req.param('id')})) export default authors

// index.ts import authors from './authors' import books from './books'

const app = new Hono() app.route('/authors', authors) app.route('/books', books) export default app

Type-Safe RPC

Share API types between server and client:

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

const route = app.post('/posts', zValidator('form', z.object({ title: z.string(), body: z.string() })), (c) => c.json({ ok: true, message: 'Created!' }, 201) ) export type AppType = typeof route

// client.ts import { hc } from 'hono/client' import type { AppType } from './server'

const client = hc<AppType>('http://localhost:8787/') const res = await client.posts.$post({ form: { title: 'Hello', body: 'World' } })

Key RPC rule: chain route definitions for type inference to work.

Validation

import { validator } from 'hono/validator'

app.post('/posts', validator('json', (value, c) => { if (!value.title) return c.text('Invalid!', 400) return { title: value.title } }), (c) => { const { title } = c.req.valid('json') return c.json({ title }, 201) } )

Validation targets: json , form , query , header , param , cookie .

Presets

Preset Import Use Case

hono (default) import { Hono } from 'hono'

Most cases, long-lived servers

hono/quick

import { Hono } from 'hono/quick'

Per-request initialization

hono/tiny

import { Hono } from 'hono/tiny'

Under 14KB, resource-limited

Platform Handler Patterns

// Cloudflare Workers / Bun - export default export default app

// Node.js import { serve } from '@hono/node-server' serve(app)

// AWS Lambda import { handle } from 'hono/aws-lambda' export const handler = handle(app)

// Deno Deno.serve(app.fetch)

// Vercel / Next.js import { handle } from 'hono/vercel' export const GET = handle(app) export const POST = handle(app)

// Netlify import { handle } from 'hono/netlify' export default handle(app)

Testing

// Use app.request() for testing 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' }, })

// Mock env (3rd argument) const res = await app.request('/posts', {}, { API_KEY: 'test' })

Key Rules

  • Don't create RoR-like controllers - define handlers inline for type inference

  • Chain routes for RPC type inference to work: const app = new Hono().get(...).post(...)

  • Middleware order matters - registered first runs first (before next), last (after next)

  • Export typeof route not typeof app for RPC

  • For RPC with app.route() : chain the .route() calls and export the chained result: const routes = app.route('/a', a).route('/b', b); export type AppType = typeof routes

  • Use lowercase header names when validating headers

  • Set Content-Type header when testing json or form validators

  • next() never throws - Hono catches errors and passes to app.onError()

  • Route registration order matters - register sub-routes before mounting with app.route()

Common Errors

  • Empty body in validator: Missing Content-Type header in request

  • RPC types not working: Routes not chained, or Hono version mismatch between client/server

  • 404 on sub-routes: Routes registered after app.route() call (wrong order)

  • Streaming not working on CF Workers: Add c.header('Content-Encoding', 'Identity')

  • WebSocket + CORS conflict: upgradeWebSocket() modifies headers internally, conflicts with header-modifying middleware

  • Slow IDE with RPC: Too many routes cause excessive type instantiation. Fix: ensure matching Hono versions, split clients per sub-app, or pre-compile types with hcWithType pattern (see references/rpc-validation.md Section 21)

Reference Files

  • references/api-reference.md

  • Context, HonoRequest, App, HTTPException, Routing

  • references/middleware-auth.md

  • Middleware concepts, Auth (Basic, Bearer, JWT, JWK)

  • references/middleware-security.md

  • Security (CORS, CSRF, Secure Headers, IP Restriction), Access Control (Combine), Custom Middleware, Best Practices

  • references/middleware-request-response.md

  • Request Processing (BodyLimit, Compress, MethodOverride, TrailingSlash), Response Processing (Cache, ETag, PrettyJSON)

  • references/middleware-utilities.md

  • Utilities (ContextStorage, Logger, RequestID, Timing, Timeout), Rendering (JSXRenderer), i18n (Language)

  • references/helpers-auth-streaming.md

  • Cookie, JWT (sign/verify/decode, all algorithms), Streaming (stream, streamText, streamSSE), WebSocket

  • references/helpers-rendering.md

  • HTML (tagged templates, raw, XSS protection), CSS (scoped styles, keyframes, cx, global styles, CSP nonce)

  • references/helpers-factory-testing.md

  • Factory, Testing (testClient), Proxy, SSG

  • references/helpers-runtime.md

  • Accepts (content negotiation), Adapter (env, getRuntimeKey), ConnInfo, Dev (showRoutes), Route

  • references/platforms-core.md

  • Cloudflare Workers, Cloudflare Pages, Bun, Deno, Node.js

  • references/platforms-serverless.md

  • AWS Lambda, Lambda@Edge, Vercel, Next.js, Netlify

  • references/platforms-other.md

  • Azure, GCR, Fastly, Supabase, Alibaba, Service Worker, WebAssembly, Platform Comparison

  • references/rpc-validation.md

  • RPC client, validators, Zod, Standard Schema

  • references/jsx.md

  • JSX, Client Components, JSX Renderer, Suspense, streaming

  • references/patterns.md

  • Best practices, testing, error handling, validation patterns, RPC troubleshooting (hcWithType), View Transitions, Service Worker

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

orpc-guide

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

tanstack-router-guide

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

tanstack-hotkeys-guide

No summary provided by upstream source.

Repository SourceNeeds Review