src
Hono is a lightweight, multi-runtime web framework for building HTTP APIs and web applications. It provides a complete stack including routing, middleware, JSX rendering (both server-side and client-side), a type-safe RPC client, and request validation — all designed to run on Cloudflare Workers, Deno, Bun, AWS Lambda, Vercel, Netlify, Node.js, and Service Workers with zero runtime-specific lock-in.
Tech Stack
Key Features
- Multi-Algorithm Router System
- Middleware Composition Engine
- Multi-Runtime Adapters
- Built-in JSX Engine with SSR and Client-Side DOM
- Type-Safe RPC Client (hc)
- Request Validation Framework
- Built-in Security & Auth Middleware
- Helper Utilities
- Router Presets
Entry Points
Start here when working with this codebase
src/index.tsApplication entry point
Start here to understand how the application initializes
Coding Conventions
Standards and patterns used in this codebase
Use extensive generic type parameters for type-safe routing, middleware composition, and request/response handling. Types are deeply nested and leverage conditional types, mapped types, and template literal types for full inference.
class Hono<E extends Env = Env, S extends Schema = {}, BasePath extends string = '/'>Disable TypeScript type-checking ESLint rules (no @typescript-eslint/recommended-type-checked) in favor of relying on the TypeScript compiler itself for type safety.
tseslint.configs.recommended (not recommended-type-checked)Follow a strategy pattern for routers: define a common Router interface and provide multiple implementations (RegExp, Trie, Smart, Linear, Pattern) that can be swapped via presets.
export interface Router<T> { name: string; add(method: string, path: string, handler: T): void; match(method: string, path: string): Result<T>; }Compose middleware using a koa-compose pattern where each middleware calls `await next()` to pass control to the next handler, with error handling wrapping the entire chain.
const composed = compose<Context>(middleware, onError, onNotFound)Use barrel exports (index.ts) as the public API surface, re-exporting only the intended public types and classes. Internal implementation details are not exported from the main entry point.
export { Hono } from './hono'; export type { Context } from './context';Use a custom HTTPException class that extends Error, carrying HTTP status codes and optional Response objects, enabling structured error responses throughout the framework.
throw new HTTPException(401, { message: 'Unauthorized', res: unauthorizedResponse })Context class provides a fluent, chainable API with shorthand methods for common response types (json, text, html, redirect, body, header, status) that return Response objects.
return c.json({ message: 'hello' }, 200)Use Vitest as the test framework with multiple test projects configured for different JSX runtimes and environments. Coverage is configured with v8 provider.
projects: [{ test: { include: ['src/**/*.test.ts'] } }, { test: { include: ['src/jsx/**/*.test.ts'] } }]Design for multi-runtime support (Bun, Deno, Node, Cloudflare Workers, AWS Lambda, etc.) by using Web Standard APIs (Request, Response, fetch) as the common interface, with thin runtime-specific adapters.
export const handle = (app: Hono) => (req: Request) => app.fetch(req)Prefer immutable patterns and lazy evaluation. Request parsing methods (query, param, json, formData) use lazy getters with caching to avoid redundant parsing.
get query() { return this.#query ??= Object.fromEntries(new URL(this.url).searchParams) }Use single-letter or short generic type parameter names following conventions: E for Env, S for Schema, P for Path, I for Input, R for Response, H for Handler.
type Handler<E extends Env = any, P extends string = any, I extends Input = {}, R extends HandlerResponse<any> = any>Use private class fields (# syntax) for internal state rather than TypeScript's private keyword.
#body: BodyInit | null; #notFoundHandler: NotFoundHandler; #path: string = '/';