# Hono - Ultrafast Web Framework Hono (meaning "flame" in Japanese) is a small, simple, and ultrafast web framework built on Web Standards. It works on any JavaScript runtime including Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Netlify, AWS Lambda, Lambda@Edge, and Node.js. The framework provides first-class TypeScript support with type inference for route parameters, request validation, and RPC clients. Hono is designed to be lightweight (under 14KB with the `hono/tiny` preset) with zero dependencies, using only Web Standards APIs. It features multiple router implementations (RegExpRouter for speed, LinearRouter for quick registration, SmartRouter for flexibility), built-in middleware for common tasks like authentication, CORS, and logging, plus a JSX renderer for server-side HTML generation. The RPC feature enables end-to-end type safety between server and client. ## Basic Application Setup Create a new Hono application instance and define route handlers using HTTP method functions. Export the app for your runtime environment. ```typescript import { Hono } from 'hono' const app = new Hono() app.get('/', (c) => c.text('Hello Hono!')) app.post('/posts', (c) => c.json({ message: 'Created!' }, 201)) app.put('/posts/:id', (c) => c.text(`Updated ${c.req.param('id')}`)) app.delete('/posts/:id', (c) => c.text(`Deleted ${c.req.param('id')}`)) // Handle any HTTP method app.all('/hello', (c) => c.text('Any Method /hello')) // Custom HTTP method app.on('PURGE', '/cache', (c) => c.text('Cache purged')) // Multiple methods on same path app.on(['PUT', 'DELETE'], '/resource', (c) => c.text('PUT or DELETE')) export default app ``` ## Path Parameters and Query Strings Access URL path parameters with `c.req.param()` and query string values with `c.req.query()`. Hono provides full TypeScript inference for parameter names. ```typescript import { Hono } from 'hono' const app = new Hono() // Single path parameter app.get('/user/:name', (c) => { const name = c.req.param('name') // TypeScript knows this is string return c.text(`Hello, ${name}!`) }) // Multiple path parameters app.get('/posts/:postId/comments/:commentId', (c) => { const { postId, commentId } = c.req.param() return c.json({ postId, commentId }) }) // Optional parameter app.get('/api/animal/:type?', (c) => { const type = c.req.param('type') ?? 'unknown' return c.text(`Animal type: ${type}`) }) // Regex pattern in parameter app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => { const { date, title } = c.req.param() return c.json({ date, title }) }) // Query parameters app.get('/search', (c) => { const query = c.req.query('q') const { limit, offset } = c.req.query() return c.json({ query, limit, offset }) }) // Multiple values for same query key (?tags=A&tags=B) app.get('/filter', (c) => { const tags = c.req.queries('tags') // string[] return c.json({ tags }) }) ``` ## Context Response Methods The Context object provides methods for returning different response types with proper Content-Type headers. ```typescript import { Hono } from 'hono' const app = new Hono() // Plain text response app.get('/text', (c) => c.text('Hello World')) // JSON response app.get('/json', (c) => { return c.json({ message: 'Hello', timestamp: Date.now() }) }) // HTML response app.get('/html', (c) => { return c.html('

Hello Hono!

') }) // Set status code app.post('/posts', (c) => { c.status(201) return c.json({ message: 'Created!' }) }) // Set custom headers app.get('/custom', (c) => { c.header('X-Custom-Header', 'CustomValue') c.header('Cache-Control', 'no-cache') return c.text('With custom headers') }) // Redirect app.get('/old-page', (c) => c.redirect('/new-page')) app.get('/moved', (c) => c.redirect('/new-location', 301)) // Body with status and headers in one call app.get('/full', (c) => { return c.body('Response body', 200, { 'X-Message': 'Hello', 'Content-Type': 'text/plain' }) }) ``` ## Request Body Parsing Parse different request body formats using the HonoRequest methods. ```typescript import { Hono } from 'hono' const app = new Hono() // JSON body app.post('/api/data', async (c) => { const body = await c.req.json() return c.json({ received: body }) }) // Form data (multipart/form-data or application/x-www-form-urlencoded) app.post('/form', async (c) => { const body = await c.req.parseBody() const name = body['name'] // string | File return c.text(`Name: ${name}`) }) // Multiple files with same field name app.post('/upload-multiple', async (c) => { const body = await c.req.parseBody() const files = body['files[]'] // (string | File)[] return c.json({ fileCount: Array.isArray(files) ? files.length : 1 }) }) // All form fields including multiple values app.post('/form-all', async (c) => { const body = await c.req.parseBody({ all: true }) return c.json(body) }) // Plain text body app.post('/text', async (c) => { const text = await c.req.text() return c.text(`Received: ${text}`) }) // Raw request headers app.get('/headers', (c) => { const userAgent = c.req.header('User-Agent') const contentType = c.req.header('Content-Type') return c.json({ userAgent, contentType }) }) ``` ## Route Grouping Organize routes into groups using sub-applications with the `route()` method or `basePath()`. ```typescript import { Hono } from 'hono' // Create sub-applications const books = new Hono() books.get('/', (c) => c.json({ books: [] })) books.get('/:id', (c) => c.json({ id: c.req.param('id') })) books.post('/', (c) => c.json({ message: 'Book created' }, 201)) const users = new Hono().basePath('/users') users.get('/', (c) => c.json({ users: [] })) users.get('/:id', (c) => c.json({ id: c.req.param('id') })) users.post('/', (c) => c.json({ message: 'User created' }, 201)) // Main application const app = new Hono() // Mount sub-applications app.route('/books', books) // /books, /books/:id app.route('/', users) // /users, /users/:id // API versioning with basePath const v1 = new Hono().basePath('/api/v1') v1.get('/status', (c) => c.json({ version: 'v1' })) const v2 = new Hono().basePath('/api/v2') v2.get('/status', (c) => c.json({ version: 'v2' })) app.route('/', v1) app.route('/', v2) export default app ``` ## Middleware Usage Apply middleware globally or to specific routes. Middleware executes in registration order with `next()` controlling flow. ```typescript import { Hono } from 'hono' import { logger } from 'hono/logger' import { cors } from 'hono/cors' import { basicAuth } from 'hono/basic-auth' import { etag } from 'hono/etag' const app = new Hono() // Global middleware (all routes) app.use(logger()) app.use(etag()) // Path-specific middleware app.use('/api/*', cors()) // Multiple middleware with specific path app.use( '/admin/*', cors({ origin: 'https://admin.example.com', credentials: true }), basicAuth({ username: 'admin', password: 'secret' }) ) // Inline custom middleware app.use(async (c, next) => { const start = Date.now() await next() const ms = Date.now() - start c.header('X-Response-Time', `${ms}ms`) }) // Middleware on specific route handler app.get( '/protected', basicAuth({ username: 'user', password: 'pass' }), (c) => c.text('Protected content') ) app.get('/', (c) => c.text('Hello!')) app.get('/api/data', (c) => c.json({ data: 'value' })) app.get('/admin/dashboard', (c) => c.text('Admin Dashboard')) export default app ``` ## Custom Middleware Creation Create reusable middleware using `createMiddleware` from the factory helper for proper TypeScript types. ```typescript import { Hono } from 'hono' import { createMiddleware } from 'hono/factory' // Simple middleware const timing = createMiddleware(async (c, next) => { const start = Date.now() await next() const duration = Date.now() - start c.res.headers.set('X-Response-Time', `${duration}ms`) }) // Middleware with parameters const rateLimit = (limit: number) => { const requests = new Map() return createMiddleware(async (c, next) => { const ip = c.req.header('CF-Connecting-IP') || 'unknown' const count = requests.get(ip) || 0 if (count >= limit) { return c.json({ error: 'Rate limit exceeded' }, 429) } requests.set(ip, count + 1) await next() }) } // Middleware that sets variables type Variables = { user: { id: string; name: string } } const auth = createMiddleware<{ Variables: Variables }>(async (c, next) => { const token = c.req.header('Authorization') if (!token) { return c.json({ error: 'Unauthorized' }, 401) } // Validate token and set user c.set('user', { id: '123', name: 'John' }) await next() }) const app = new Hono<{ Variables: Variables }>() app.use(timing) app.use('/api/*', rateLimit(100)) app.use('/protected/*', auth) app.get('/protected/profile', (c) => { const user = c.get('user') return c.json(user) }) export default app ``` ## Context Variables (set/get) Share data between middleware and handlers using context variables with type safety. ```typescript import { Hono } from 'hono' import { createMiddleware } from 'hono/factory' type Variables = { requestId: string user: { id: string; role: string } | null startTime: number } const app = new Hono<{ Variables: Variables }>() // Set variables in middleware app.use(async (c, next) => { c.set('requestId', crypto.randomUUID()) c.set('startTime', Date.now()) c.set('user', null) await next() }) // Authentication middleware sets user const authMiddleware = createMiddleware<{ Variables: Variables }>( async (c, next) => { const token = c.req.header('Authorization') if (token === 'Bearer valid-token') { c.set('user', { id: '1', role: 'admin' }) } await next() } ) app.use('/api/*', authMiddleware) // Access variables in handlers app.get('/api/profile', (c) => { const user = c.get('user') const requestId = c.get('requestId') if (!user) { return c.json({ error: 'Not authenticated', requestId }, 401) } return c.json({ user, requestId }) }) // Access via c.var shorthand app.get('/api/debug', (c) => { const elapsed = Date.now() - c.var.startTime return c.json({ requestId: c.var.requestId, elapsedMs: elapsed, authenticated: c.var.user !== null }) }) export default app ``` ## Error Handling Handle errors globally with `app.onError()` and customize 404 responses with `app.notFound()`. ```typescript import { Hono } from 'hono' import { HTTPException } from 'hono/http-exception' const app = new Hono() // Custom 404 handler app.notFound((c) => { return c.json({ error: 'Not Found', message: `Route ${c.req.path} does not exist`, status: 404 }, 404) }) // Global error handler app.onError((err, c) => { console.error(`Error: ${err.message}`) if (err instanceof HTTPException) { return c.json({ error: err.message, status: err.status }, err.status) } return c.json({ error: 'Internal Server Error', message: err.message, status: 500 }, 500) }) // Throw HTTPException for controlled errors app.get('/item/:id', async (c) => { const id = c.req.param('id') const item = await findItem(id) if (!item) { throw new HTTPException(404, { message: 'Item not found' }) } return c.json(item) }) // Validation error example app.post('/users', async (c) => { const body = await c.req.json() if (!body.email) { throw new HTTPException(400, { message: 'Email is required' }) } return c.json({ message: 'User created' }, 201) }) async function findItem(id: string) { return id === '1' ? { id, name: 'Item 1' } : null } export default app ``` ## Request Validation Validate request data using the built-in validator or third-party libraries like Zod. ```typescript import { Hono } from 'hono' import { validator } from 'hono/validator' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const app = new Hono() // Manual validation app.post( '/posts', validator('json', (value, c) => { const { title, body } = value as { title?: string; body?: string } if (!title || typeof title !== 'string') { return c.json({ error: 'Title is required' }, 400) } if (!body || typeof body !== 'string') { return c.json({ error: 'Body is required' }, 400) } return { title, body } }), (c) => { const { title, body } = c.req.valid('json') return c.json({ message: 'Created', title, body }, 201) } ) // Zod validation const createUserSchema = z.object({ email: z.string().email(), name: z.string().min(2), age: z.number().min(0).optional() }) app.post( '/users', zValidator('json', createUserSchema), (c) => { const user = c.req.valid('json') // user is fully typed: { email: string; name: string; age?: number } return c.json({ message: 'User created', user }, 201) } ) // Validate multiple targets const querySchema = z.object({ page: z.coerce.number().default(1), limit: z.coerce.number().default(10) }) app.get( '/items/:category', validator('param', (value, c) => { const category = value['category'] if (!['books', 'movies', 'music'].includes(category)) { return c.json({ error: 'Invalid category' }, 400) } return { category } }), zValidator('query', querySchema), (c) => { const { category } = c.req.valid('param') const { page, limit } = c.req.valid('query') return c.json({ category, page, limit }) } ) export default app ``` ## CORS Middleware Configure Cross-Origin Resource Sharing with the built-in CORS middleware. ```typescript import { Hono } from 'hono' import { cors } from 'hono/cors' const app = new Hono() // Allow all origins (default) app.use('/public/*', cors()) // Specific origin app.use('/api/*', cors({ origin: 'https://example.com', allowMethods: ['GET', 'POST', 'PUT', 'DELETE'], allowHeaders: ['Content-Type', 'Authorization'], exposeHeaders: ['X-Total-Count'], maxAge: 600, credentials: true })) // Multiple origins app.use('/shared/*', cors({ origin: ['https://app.example.com', 'https://admin.example.com'] })) // Dynamic origin based on request app.use('/dynamic/*', cors({ origin: (origin, c) => { if (origin.endsWith('.example.com')) { return origin } return 'https://example.com' } })) // Environment-based configuration app.use('/env/*', async (c, next) => { const corsMiddleware = cors({ origin: c.env.CORS_ORIGIN || '*' }) return corsMiddleware(c, next) }) app.get('/public/data', (c) => c.json({ public: true })) app.get('/api/data', (c) => c.json({ api: true })) export default app ``` ## JWT Authentication Protect routes with JWT authentication using the built-in JWT middleware. ```typescript import { Hono } from 'hono' import { jwt, sign, verify } from 'hono/jwt' import type { JwtVariables } from 'hono/jwt' type Variables = JwtVariables const app = new Hono<{ Variables: Variables }>() const JWT_SECRET = 'your-secret-key' // Login endpoint - generate token app.post('/login', async (c) => { const { username, password } = await c.req.json() // Validate credentials (simplified) if (username === 'admin' && password === 'password') { const payload = { sub: username, role: 'admin', exp: Math.floor(Date.now() / 1000) + 60 * 60 // 1 hour } const token = await sign(payload, JWT_SECRET) return c.json({ token }) } return c.json({ error: 'Invalid credentials' }, 401) }) // Protect routes with JWT middleware app.use('/api/*', jwt({ secret: JWT_SECRET, alg: 'HS256' })) // Access JWT payload in protected routes app.get('/api/profile', (c) => { const payload = c.get('jwtPayload') return c.json({ username: payload.sub, role: payload.role }) }) // JWT from cookie instead of header app.use('/dashboard/*', jwt({ secret: JWT_SECRET, alg: 'HS256', cookie: 'auth_token' })) // Custom header name app.use('/custom/*', jwt({ secret: JWT_SECRET, alg: 'HS256', headerName: 'X-Auth-Token' })) // Using environment variable for secret app.use('/secure/*', async (c, next) => { const jwtMiddleware = jwt({ secret: c.env.JWT_SECRET, alg: 'HS256' }) return jwtMiddleware(c, next) }) export default app ``` ## Cookie Helper Manage HTTP cookies with get, set, and delete operations including signed cookies. ```typescript import { Hono } from 'hono' import { getCookie, setCookie, deleteCookie, getSignedCookie, setSignedCookie } from 'hono/cookie' const app = new Hono() const COOKIE_SECRET = 'super-secret-key' // Set a cookie app.post('/login', async (c) => { const { username } = await c.req.json() setCookie(c, 'session', username, { path: '/', secure: true, httpOnly: true, maxAge: 60 * 60 * 24, // 1 day sameSite: 'Lax' }) return c.json({ message: 'Logged in' }) }) // Get a cookie app.get('/profile', (c) => { const session = getCookie(c, 'session') if (!session) { return c.json({ error: 'Not logged in' }, 401) } return c.json({ username: session }) }) // Delete a cookie app.post('/logout', (c) => { deleteCookie(c, 'session', { path: '/', secure: true }) return c.json({ message: 'Logged out' }) }) // Signed cookies (tamper-proof) app.post('/set-signed', async (c) => { await setSignedCookie(c, 'user_data', 'user123', COOKIE_SECRET, { path: '/', httpOnly: true }) return c.json({ message: 'Signed cookie set' }) }) app.get('/get-signed', async (c) => { const userData = await getSignedCookie(c, COOKIE_SECRET, 'user_data') if (userData === false) { return c.json({ error: 'Cookie tampered or invalid' }, 400) } return c.json({ userData }) }) // Get all cookies app.get('/all-cookies', (c) => { const cookies = getCookie(c) return c.json(cookies) }) export default app ``` ## Streaming Responses Stream data to clients using text streams and Server-Sent Events (SSE). ```typescript import { Hono } from 'hono' import { stream, streamText, streamSSE } from 'hono/streaming' const app = new Hono() // Basic streaming app.get('/stream', (c) => { return stream(c, async (stream) => { stream.onAbort(() => { console.log('Client disconnected') }) await stream.write(new Uint8Array([72, 101, 108, 108, 111])) // "Hello" await stream.write(new Uint8Array([32, 87, 111, 114, 108, 100])) // " World" }) }) // Text streaming (for AI-like responses) app.get('/stream-text', (c) => { return streamText(c, async (stream) => { const words = ['Hello', 'this', 'is', 'a', 'streaming', 'response'] for (const word of words) { await stream.write(word + ' ') await stream.sleep(200) // Simulate delay } await stream.writeln('') // New line at end }) }) // Server-Sent Events app.get('/sse', (c) => { return streamSSE(c, async (stream) => { let id = 0 while (true) { await stream.writeSSE({ data: JSON.stringify({ time: new Date().toISOString(), count: id }), event: 'update', id: String(id++) }) await stream.sleep(1000) } }) }) // Stream with error handling app.get('/stream-safe', (c) => { return stream( c, async (stream) => { await stream.write(new Uint8Array([1, 2, 3])) // Potentially failing operation throw new Error('Something went wrong') }, (err, stream) => { console.error('Stream error:', err) stream.writeln('Error occurred during streaming') } ) }) export default app ``` ## JSX Server-Side Rendering Render HTML using JSX syntax for server-side rendering. ```tsx import { Hono } from 'hono' import type { FC } from 'hono/jsx' const app = new Hono() // Layout component const Layout: FC<{ title: string }> = ({ title, children }) => ( {title} {children} ) // Page component const HomePage: FC<{ name: string }> = ({ name }) => (

Welcome, {name}!

This is rendered with Hono JSX.

) // List component const UserList: FC<{ users: string[] }> = ({ users }) => (

User List

) app.get('/', (c) => { return c.html() }) app.get('/users', (c) => { const users = ['Alice', 'Bob', 'Charlie'] return c.html() }) // Async component const AsyncData: FC = async () => { await new Promise((r) => setTimeout(r, 100)) const data = { message: 'Loaded!' } return
{data.message}
} app.get('/async', (c) => { return c.html( ) }) export default app ``` ## RPC Client (Type-Safe API Calls) Share API types between server and client for end-to-end type safety using Hono's RPC feature. ```typescript // server.ts import { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const app = new Hono() const postSchema = z.object({ title: z.string(), body: z.string() }) const routes = app .get('/posts', (c) => { return c.json({ posts: [ { id: 1, title: 'Hello' }, { id: 2, title: 'World' } ] }) }) .get('/posts/:id', (c) => { const id = c.req.param('id') return c.json({ id, title: `Post ${id}` }) }) .post('/posts', zValidator('json', postSchema), (c) => { const { title, body } = c.req.valid('json') return c.json({ id: 3, title, body }, 201) }) export type AppType = typeof routes export default app // client.ts import { hc } from 'hono/client' import type { AppType } from './server' import type { InferRequestType, InferResponseType } from 'hono/client' const client = hc('http://localhost:8787') // GET request - TypeScript knows the response shape async function getPosts() { const res = await client.posts.$get() if (res.ok) { const data = await res.json() console.log(data.posts) // { id: number; title: string }[] } } // GET with path parameter async function getPost(id: string) { const res = await client.posts[':id'].$get({ param: { id } }) if (res.ok) { const data = await res.json() console.log(data.title) } } // POST with typed body async function createPost(title: string, body: string) { const res = await client.posts.$post({ json: { title, body } }) if (res.ok) { const data = await res.json() console.log(data.id) // TypeScript knows this is number } } // Infer request/response types type PostRequest = InferRequestType['json'] type PostResponse = InferResponseType ``` ## Testing with app.request() Test Hono applications using the built-in `app.request()` method without starting a server. ```typescript import { Hono } from 'hono' import { describe, it, expect } from 'vitest' const app = new Hono() app.get('/hello', (c) => c.text('Hello World')) app.get('/json', (c) => c.json({ message: 'Hello' })) app.post('/echo', async (c) => { const body = await c.req.json() return c.json(body, 201) }) app.get('/user/:id', (c) => { return c.json({ id: c.req.param('id') }) }) describe('Hono App', () => { it('GET /hello returns text', async () => { const res = await app.request('/hello') expect(res.status).toBe(200) expect(await res.text()).toBe('Hello World') }) it('GET /json returns JSON', async () => { const res = await app.request('/json') expect(res.status).toBe(200) expect(await res.json()).toEqual({ message: 'Hello' }) }) it('POST /echo returns body', async () => { const res = await app.request('/echo', { method: 'POST', body: JSON.stringify({ test: 'data' }), headers: { 'Content-Type': 'application/json' } }) expect(res.status).toBe(201) expect(await res.json()).toEqual({ test: 'data' }) }) it('GET /user/:id returns param', async () => { const res = await app.request('/user/123') expect(res.status).toBe(200) expect(await res.json()).toEqual({ id: '123' }) }) // Test with mock environment it('works with env bindings', async () => { const appWithEnv = new Hono<{ Bindings: { API_KEY: string } }>() appWithEnv.get('/key', (c) => c.text(c.env.API_KEY)) const res = await appWithEnv.request('/key', {}, { API_KEY: 'secret' }) expect(await res.text()).toBe('secret') }) }) ``` ## Cloudflare Workers Bindings Access Cloudflare Workers environment bindings (KV, R2, D1, Durable Objects) with proper TypeScript types. ```typescript import { Hono } from 'hono' type Bindings = { // Environment variables API_KEY: string ENVIRONMENT: string // KV Namespace MY_KV: KVNamespace // R2 Bucket MY_BUCKET: R2Bucket // D1 Database MY_DB: D1Database } const app = new Hono<{ Bindings: Bindings }>() // Access environment variables app.get('/config', (c) => { return c.json({ environment: c.env.ENVIRONMENT, hasApiKey: !!c.env.API_KEY }) }) // KV operations app.get('/kv/:key', async (c) => { const key = c.req.param('key') const value = await c.env.MY_KV.get(key) if (!value) { return c.json({ error: 'Not found' }, 404) } return c.json({ key, value }) }) app.put('/kv/:key', async (c) => { const key = c.req.param('key') const { value } = await c.req.json() await c.env.MY_KV.put(key, value) return c.json({ message: 'Saved' }) }) // R2 operations app.get('/files/:key', async (c) => { const key = c.req.param('key') const object = await c.env.MY_BUCKET.get(key) if (!object) { return c.json({ error: 'File not found' }, 404) } return new Response(object.body, { headers: { 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream' } }) }) app.put('/files/:key', async (c) => { const key = c.req.param('key') const body = await c.req.arrayBuffer() await c.env.MY_BUCKET.put(key, body) return c.json({ message: 'Uploaded' }) }) // D1 queries app.get('/users', async (c) => { const { results } = await c.env.MY_DB .prepare('SELECT * FROM users LIMIT 10') .all() return c.json({ users: results }) }) app.post('/users', async (c) => { const { name, email } = await c.req.json() await c.env.MY_DB .prepare('INSERT INTO users (name, email) VALUES (?, ?)') .bind(name, email) .run() return c.json({ message: 'User created' }, 201) }) export default app ``` ## Factory Helper Use the factory helper to create middleware and handlers with shared type definitions. ```typescript import { Hono } from 'hono' import { createFactory, createMiddleware } from 'hono/factory' // Define shared types type Env = { Bindings: { DATABASE_URL: string } Variables: { db: Database user: User | null requestId: string } } interface Database { query: (sql: string) => Promise } interface User { id: string name: string } // Create factory with types const factory = createFactory() // Create typed middleware const dbMiddleware = factory.createMiddleware(async (c, next) => { const db: Database = { query: async (sql) => ({ rows: [] }) } c.set('db', db) await next() }) const authMiddleware = factory.createMiddleware(async (c, next) => { const token = c.req.header('Authorization') if (token === 'Bearer valid') { c.set('user', { id: '1', name: 'John' }) } else { c.set('user', null) } await next() }) const requestIdMiddleware = factory.createMiddleware(async (c, next) => { c.set('requestId', crypto.randomUUID()) await next() }) // Create handlers with proper types const getUsers = factory.createHandlers( dbMiddleware, async (c) => { const result = await c.var.db.query('SELECT * FROM users') return c.json({ users: result.rows, requestId: c.var.requestId }) } ) const getProfile = factory.createHandlers( authMiddleware, (c) => { const user = c.var.user if (!user) { return c.json({ error: 'Unauthorized' }, 401) } return c.json(user) } ) // Create app from factory const app = factory.createApp() app.use(requestIdMiddleware) app.get('/users', ...getUsers) app.get('/profile', ...getProfile) export default app ``` ## Summary Hono excels at building high-performance web APIs, edge applications, and full-stack TypeScript applications. Its lightweight design and Web Standards compliance make it ideal for serverless environments like Cloudflare Workers, Vercel Edge Functions, and AWS Lambda@Edge. The framework's middleware system follows familiar patterns from Express while providing full TypeScript inference, making it easy to compose authentication, validation, logging, and CORS handling. The RPC feature combined with Zod validation enables end-to-end type safety between server and client code, eliminating runtime type mismatches. Hono's JSX support allows server-side rendering without additional dependencies, while the streaming helpers enable real-time features like Server-Sent Events. Whether building a simple API proxy, a complex microservice, or a full-stack application with client-side type safety, Hono provides the performance and developer experience needed for modern web development.