# Hono Web Framework Hono is an ultrafast, lightweight web framework built on Web Standards that works across multiple JavaScript runtimes including Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, AWS Lambda, Lambda@Edge, and Node.js. The framework emphasizes simplicity, speed, and developer experience while maintaining zero dependencies and using only Web Standard APIs. With a router that achieves exceptional performance through RegExp-based matching and a modular architecture that supports tree-shaking, Hono enables developers to build efficient web applications with minimal overhead. The framework provides a comprehensive suite of built-in middleware for common tasks like authentication, CORS, compression, logging, and request validation, alongside powerful features for JSX rendering, streaming responses, server-sent events, and type-safe API clients. Hono's architecture is designed around Web Standards, making it portable across different platforms without code changes, while its TypeScript-first design ensures excellent type inference and developer tooling support throughout the entire development workflow. ## Basic Application Setup Creating and configuring a Hono application ```typescript import { Hono } from 'hono' const app = new Hono() // Basic route handler app.get('/', (c) => { return c.text('Hello Hono!') }) // JSON response app.get('/api/user', (c) => { return c.json({ id: 1, name: 'John Doe' }) }) // Route parameters app.get('/posts/:id', (c) => { const id = c.req.param('id') return c.json({ postId: id }) }) // Multiple HTTP methods app.post('/api/posts', async (c) => { const body = await c.req.json() return c.json({ success: true, data: body }, 201) }) // Export for various runtimes export default app ``` ## Type-Safe Routing with Environment and Schema Advanced routing with TypeScript type safety ```typescript import { Hono } from 'hono' type Env = { Bindings: { DB: D1Database API_KEY: string } Variables: { user: { id: string; name: string } } } const app = new Hono() // Access bindings and variables with type safety app.get('/users/:id', async (c) => { const userId = c.req.param('id') const db = c.env.DB const user = await db .prepare('SELECT * FROM users WHERE id = ?') .bind(userId) .first() return c.json(user) }) // Middleware setting typed variables app.use('*', async (c, next) => { const user = { id: '123', name: 'Alice' } c.set('user', user) await next() }) // Access typed variables in handlers app.get('/profile', (c) => { const user = c.get('user') // Type: { id: string; name: string } return c.json({ profile: user }) }) ``` ## Request Validation with Validator Middleware Validating request data with custom validation functions ```typescript import { Hono } from 'hono' import { validator } from 'hono/validator' import { z } from 'zod' const app = new Hono() // JSON body validation with Zod const postSchema = z.object({ title: z.string().min(1).max(100), content: z.string(), published: z.boolean().optional(), }) app.post( '/posts', validator('json', (value, c) => { const parsed = postSchema.safeParse(value) if (!parsed.success) { return c.json({ error: parsed.error }, 400) } return parsed.data }), (c) => { const data = c.req.valid('json') // data is typed as { title: string; content: string; published?: boolean } return c.json({ id: 123, ...data }, 201) } ) // Query parameter validation app.get( '/search', validator('query', (value, c) => { const q = value.q const page = parseInt(value.page || '1') if (page < 1 || isNaN(page)) { return c.text('Invalid page number', 400) } return { q, page } }), (c) => { const { q, page } = c.req.valid('query') return c.json({ query: q, page, results: [] }) } ) ``` ## CORS Middleware Configuring Cross-Origin Resource Sharing ```typescript import { Hono } from 'hono' import { cors } from 'hono/cors' const app = new Hono() // Basic CORS - allow all origins app.use('/api/*', cors()) // Advanced CORS configuration app.use( '/api/v2/*', cors({ origin: ['https://example.com', 'https://app.example.com'], allowMethods: ['POST', 'GET', 'OPTIONS', 'PUT', 'DELETE'], allowHeaders: ['X-Custom-Header', 'Content-Type', 'Authorization'], exposeHeaders: ['Content-Length', 'X-Request-Id'], maxAge: 600, credentials: true, }) ) // Dynamic origin validation app.use( '/api/v3/*', cors({ origin: async (origin, c) => { // Check if origin is in allowed list from database const allowed = await checkOrigin(origin) return allowed ? origin : null }, }) ) app.get('/api/data', (c) => { return c.json({ message: 'CORS enabled' }) }) ``` ## Basic Authentication Middleware Protecting routes with HTTP Basic Authentication ```typescript import { Hono } from 'hono' import { basicAuth } from 'hono/basic-auth' const app = new Hono() // Simple username/password authentication app.use( '/admin/*', basicAuth({ username: 'admin', password: 'secret123', }) ) // Custom verification function app.use( '/api/secure/*', basicAuth({ verifyUser: async (username, password, c) => { // Verify against database const user = await db.users.findOne({ username }) if (!user) return false return await bcrypt.compare(password, user.passwordHash) }, realm: 'Secure API', invalidUserMessage: 'Authentication failed', }) ) app.get('/admin/dashboard', (c) => { return c.text('Admin Dashboard') }) ``` ## JWT Authentication Middleware Token-based authentication with JSON Web Tokens ```typescript import { Hono } from 'hono' import { jwt, sign, verify } from 'hono/jwt' const app = new Hono() // Protect routes with JWT app.use( '/api/*', jwt({ secret: 'your-secret-key', }) ) // Issue JWT token on login app.post('/auth/login', async (c) => { const { username, password } = await c.req.json() // Verify credentials (simplified) if (username === 'user' && password === 'pass') { const token = await sign( { sub: 'user123', name: username, exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour }, 'your-secret-key' ) return c.json({ token }) } return c.json({ error: 'Invalid credentials' }, 401) }) // Access JWT payload in protected routes app.get('/api/profile', (c) => { const payload = c.get('jwtPayload') return c.json({ userId: payload.sub, name: payload.name }) }) ``` ## Logger Middleware Request logging with timing information ```typescript import { Hono } from 'hono' import { logger } from 'hono/logger' const app = new Hono() // Basic logging to console app.use(logger()) // Custom log function app.use(logger((message) => { // Send logs to external service console.log(`[${new Date().toISOString()}] ${message}`) })) app.get('/api/users', (c) => { return c.json([{ id: 1, name: 'Alice' }]) }) // Output example: // --> GET /api/users // <-- GET /api/users 200 15ms ``` ## Cookie Management Reading and setting cookies with security options ```typescript import { Hono } from 'hono' import { getCookie, setCookie, deleteCookie } from 'hono/cookie' const app = new Hono() app.get('/cookies/set', (c) => { setCookie(c, 'session_id', 'abc123', { maxAge: 3600, httpOnly: true, secure: true, sameSite: 'Strict', }) // Set secure cookie with prefix setCookie(c, 'auth_token', 'xyz789', { prefix: 'secure', maxAge: 7200, httpOnly: true, }) return c.text('Cookies set') }) app.get('/cookies/get', (c) => { const sessionId = getCookie(c, 'session_id') const authToken = getCookie(c, 'auth_token', 'secure') const allCookies = getCookie(c) return c.json({ sessionId, authToken, all: allCookies }) }) app.get('/cookies/delete', (c) => { deleteCookie(c, 'session_id') return c.text('Cookie deleted') }) ``` ## Streaming Responses Streaming data with Server-Sent Events ```typescript import { Hono } from 'hono' import { streamSSE } from 'hono/streaming' const app = new Hono() // Server-Sent Events stream app.get('/events', (c) => { return streamSSE(c, async (stream) => { let id = 0 while (true) { const message = `Message ${id}` await stream.writeSSE({ data: message, event: 'update', id: String(id), }) id++ await new Promise((resolve) => setTimeout(resolve, 1000)) if (id >= 10) { break } } }) }) // Text streaming import { streamText } from 'hono/streaming' app.get('/stream-text', (c) => { return streamText(c, async (stream) => { await stream.write('Hello ') await stream.write('World') await stream.write('!') }) }) ``` ## JSX Rendering Server-side rendering with JSX ```typescript import { Hono } from 'hono' import { jsxRenderer } from 'hono/jsx-renderer' import type { FC } from 'hono/jsx' const app = new Hono() // Layout component const Layout: FC = (props) => { return ( {props.title || 'My App'} {props.children} ) } // Configure JSX renderer app.use( '*', jsxRenderer(({ children, title }) => { return (
{children}
) }) ) // Render JSX in routes app.get('/', (c) => { return c.render(

Welcome to Hono

Fast, lightweight web framework

, { title: 'Home' } ) }) app.get('/users/:id', (c) => { const userId = c.req.param('id') return c.render(

User {userId}

User profile page

, { title: `User ${userId}` } ) }) ``` ## HTTP Exception Handling Custom error handling with HTTPException ```typescript import { Hono } from 'hono' import { HTTPException } from 'hono/http-exception' const app = new Hono() // Throw HTTPException in handlers app.post('/auth', async (c) => { const { username, password } = await c.req.json() if (!username || !password) { throw new HTTPException(400, { message: 'Username and password are required' }) } const authorized = await verifyCredentials(username, password) if (!authorized) { throw new HTTPException(401, { message: 'Invalid credentials', res: new Response('Unauthorized', { status: 401, headers: { 'X-Custom-Header': 'value' }, }), }) } return c.json({ success: true }) }) // Custom error handler app.onError((err, c) => { if (err instanceof HTTPException) { return err.getResponse() } console.error(err) return c.json({ error: 'Internal Server Error' }, 500) }) // Not found handler app.notFound((c) => { return c.json({ error: 'Route not found' }, 404) }) ``` ## Route Grouping and Mounting Organizing routes with basePath and route mounting ```typescript import { Hono } from 'hono' const app = new Hono() // Create sub-application for API v1 const apiV1 = new Hono() apiV1.get('/users', (c) => c.json([{ id: 1, name: 'Alice' }])) apiV1.get('/posts', (c) => c.json([{ id: 1, title: 'Hello' }])) // Create sub-application for API v2 const apiV2 = new Hono() apiV2.get('/users', (c) => c.json([{ id: 1, name: 'Alice', email: 'alice@example.com' }])) // Mount sub-applications app.route('/api/v1', apiV1) app.route('/api/v2', apiV2) // Create admin routes with authentication const adminRoutes = new Hono() adminRoutes.use('*', async (c, next) => { // Authentication middleware const token = c.req.header('Authorization') if (!token) { return c.text('Unauthorized', 401) } await next() }) adminRoutes.get('/dashboard', (c) => c.text('Admin Dashboard')) adminRoutes.get('/users', (c) => c.json([])) app.route('/admin', adminRoutes) // Routes are now: // GET /api/v1/users // GET /api/v2/users // GET /admin/dashboard ``` ## Type-Safe HTTP Client RPC-style client for consuming Hono APIs ```typescript import { Hono } from 'hono' import { hc } from 'hono/client' // Server definition const app = new Hono() .get('/posts/:id', (c) => { const id = c.req.param('id') return c.json({ id, title: 'Hello' }) }) .post('/posts', async (c) => { const body = await c.req.json<{ title: string; content: string }>() return c.json({ id: 123, ...body }, 201) }) export type AppType = typeof app // Client usage const client = hc('http://localhost:3000') // GET request with path parameters const res1 = await client.posts[':id'].$get({ param: { id: '123' } }) const data1 = await res1.json() // data1 is typed as { id: string, title: string } // POST request with JSON body const res2 = await client.posts.$post({ json: { title: 'New Post', content: 'Content here', }, }) const data2 = await res2.json() // data2 is typed as { id: number, title: string, content: string } // Query parameters const res3 = await client.search.$get({ query: { q: 'hono', page: '1', }, }) ``` ## Middleware Composition Creating and combining custom middleware ```typescript import { Hono } from 'hono' import type { MiddlewareHandler } from 'hono' const app = new Hono() // Custom timing middleware const timing = (): MiddlewareHandler => { return async (c, next) => { const start = Date.now() await next() const ms = Date.now() - start c.res.headers.set('X-Response-Time', `${ms}ms`) } } // Custom request ID middleware const requestId = (): MiddlewareHandler => { return async (c, next) => { const id = crypto.randomUUID() c.set('requestId', id) c.res.headers.set('X-Request-ID', id) await next() } } // Rate limiting middleware const rateLimit = (max: number): MiddlewareHandler => { const requests = new Map() return async (c, next) => { const ip = c.req.header('CF-Connecting-IP') || 'unknown' const now = Date.now() const timestamps = requests.get(ip) || [] // Remove old timestamps (older than 1 minute) const recent = timestamps.filter(t => now - t < 60000) if (recent.length >= max) { return c.text('Rate limit exceeded', 429) } recent.push(now) requests.set(ip, recent) await next() } } // Apply middleware app.use('*', timing()) app.use('*', requestId()) app.use('/api/*', rateLimit(100)) app.get('/api/data', (c) => { const requestId = c.get('requestId') return c.json({ requestId, data: [] }) }) ``` ## Runtime Adapters Platform-specific integrations and serving ```typescript // Cloudflare Workers import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Hello from Cloudflare Workers!')) export default app // Bun import { Hono } from 'hono' import { serve } from '@hono/node-server' const app = new Hono() app.get('/', (c) => c.text('Hello from Bun!')) const port = 3000 console.log(`Server running on port ${port}`) export default { port, fetch: app.fetch, } // Node.js import { Hono } from 'hono' import { serve } from '@hono/node-server' const app = new Hono() app.get('/', (c) => c.text('Hello from Node.js!')) serve({ fetch: app.fetch, port: 3000, }) // AWS Lambda import { Hono } from 'hono' import { handle } from 'hono/aws-lambda' const app = new Hono() app.get('/', (c) => c.text('Hello from Lambda!')) export const handler = handle(app) // Deno import { Hono } from 'https://deno.land/x/hono/mod.ts' const app = new Hono() app.get('/', (c) => c.text('Hello from Deno!')) Deno.serve(app.fetch) ``` ## Static File Serving Serving static assets from filesystem ```typescript import { Hono } from 'hono' import { serveStatic } from 'hono/cloudflare-workers' // or from 'hono/bun' or 'hono/deno' depending on runtime const app = new Hono() // Serve files from public directory app.use('/static/*', serveStatic({ root: './' })) // Serve with custom options app.use( '/assets/*', serveStatic({ root: './public', rewriteRequestPath: (path) => path.replace(/^\/assets/, ''), }) ) // Serve single file app.get('/favicon.ico', serveStatic({ path: './public/favicon.ico' })) app.get('/', (c) => { return c.html(` `) }) ``` ## Summary Hono provides a modern, type-safe approach to building web applications that run anywhere JavaScript does. The framework's primary use cases include building REST APIs with strong type inference, creating serverless functions for edge computing platforms, developing server-side rendered applications with JSX, and implementing real-time features through streaming and SSE. Its lightweight nature makes it ideal for microservices, while the built-in middleware covers authentication, security headers, CORS, request validation, compression, and logging out of the box. Integration patterns in Hono center around composability and portability. The framework supports modular application design through route mounting and middleware chaining, enabling developers to organize code into logical sections while maintaining type safety across boundaries. The RPC-style client provides end-to-end type safety between server and client code without code generation. Platform adapters ensure the same application code runs on Cloudflare Workers, Bun, Deno, Node.js, AWS Lambda, and other runtimes with minimal configuration changes, while the validator middleware integrates seamlessly with popular validation libraries like Zod for robust request handling.