Hono — Web Framework Siêu Nhẹ Cho Edge Computing
Hono là web framework siêu nhẹ (~14KB) cho edge, chạy trên Cloudflare Workers, Deno, Bun, Node. Router nhanh, middleware, RPC type-safe.
Hono là web framework siêu nhẹ (~14KB), siêu nhanh, chạy trên mọi runtime: Cloudflare Workers, Deno, Bun, Node.js, Lambda.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Hono!'))
app.get('/json', (c) => c.json({ message: 'Hello!' }))
app.post('/api/data', async (c) => {
const body = await c.req.json()
return c.json({ received: body })
})
export default apptsTại Sao Hono?#
So Sánh Runtime#
| Express | Fastify | Hono | |
|---|---|---|---|
| Kích thước | ~200KB | ~150KB | ~14KB |
| Cloudflare Workers | ❌ | ❌ | ✅ |
| Deno | ❌ | ❌ | ✅ |
| Bun | ✅ | ❌ | ✅ |
| Node | ✅ | ✅ | ✅ |
| AWS Lambda | ❌ | ❌ | ✅ |
| Router | Regexp | Trie/Regexp | Trie (nhanh nhất) |
| TypeScript | OK | Tốt | Xuất sắc |
Hono không chỉ nhỏ — nó còn có router nhanh nhất trong các web framework JS (dùng Trie tree, không regexp).
Tốc Độ Router#
Framework Req/s
Hono (Trie) ~280k
Fastify ~220k
Express ~15kplaintextVới edge computing, mỗi microsecond quan trọng — Hono được tối ưu cho cold start.
Cài Đặt#
mkdir hono-app && cd hono-app
npm init -y
npm install hono
npx tsc --initbashCloudflare Workers#
npm create cloudflare@latest my-worker -- --template hono
cd my-worker
npm run devbashBun#
bun init -y
bun add honobashRouter#
import { Hono } from 'hono'
const app = new Hono()
// Static routes
app.get('/', (c) => c.text('Home'))
app.get('/about', (c) => c.text('About'))
// Dynamic params
app.get('/users/:id', (c) => {
const id = c.req.param('id')
return c.json({ userId: id })
})
// Query params
app.get('/search', (c) => {
const q = c.req.query('q')
const page = c.req.query('page') || '1'
return c.json({ query: q, page: Number(page) })
})
// Wildcard
app.get('/files/*', (c) => {
const path = c.req.path
return c.text(`Serving: ${path}`)
})
// Multiple handlers (middleware chain)
app.get('/profile',
async (c, next) => {
const token = c.req.header('Authorization')
if (!token) return c.text('Unauthorized', 401)
c.set('userId', '123')
await next()
},
(c) => {
const userId = c.get('userId')
return c.json({ profile: { id: userId, name: 'User' } })
}
)
export default apptsMiddleware#
Hono có middleware system giống Express nhưng nhẹ hơn:
// Built-in middleware
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { jwt } from 'hono/jwt'
import { etag } from 'hono/etag'
import { prettyJSON } from 'hono/pretty-json'
import { timeout } from 'hono/timeout'
const app = new Hono()
app.use('*', logger())
app.use('*', cors({ origin: 'https://example.com' }))
app.use('/api/*', timeout(5000))
app.use('*', prettyJSON())
app.use('/static/*', etag())tsJWT Auth#
import { jwt } from 'hono/jwt'
app.use('/admin/*', jwt({
secret: 'my-secret-key',
}))
app.get('/admin/dashboard', (c) => {
const payload = c.get('jwtPayload')
return c.json({ admin: payload })
})tsCustom Middleware#
// Rate limiter middleware
const rateLimit = (max: number, windowMs: number) => {
const hits = new Map<string, { count: number; reset: number }>()
return async (c: Context, next: Next) => {
const ip = c.req.header('CF-Connecting-IP') || 'unknown'
const now = Date.now()
const record = hits.get(ip)
if (!record || now > record.reset) {
hits.set(ip, { count: 1, reset: now + windowMs })
return next()
}
if (record.count >= max) {
return c.text('Too Many Requests', 429)
}
record.count++
return next()
}
}
app.use('/api/*', rateLimit(100, 60000))tsValidation với Zod#
import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'
const schema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
age: z.number().int().positive().optional(),
})
app.post('/users', zValidator('json', schema), (c) => {
const data = c.req.valid('json')
// data: { name: string; email: string; age?: number }
return c.json({ created: data }, 201)
})
// Validate query, param, header riêng
app.get('/posts/:id',
zValidator('param', z.object({ id: z.string().regex(/^\d+$/) })),
zValidator('query', z.object({ page: z.string().optional() })),
(c) => {
const { id } = c.req.valid('param')
const { page } = c.req.valid('query')
return c.json({ postId: id, page })
}
)tsRPC — Type-Safe Client#
Giống tRPC, Hono có RPC mode cho type safety từ server đến client:
// server.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/users/:id', (c) => {
const id = c.req.param('id')
return c.json({
id,
name: 'Alice',
email: 'alice@example.com',
})
})
app.post('/users', async (c) => {
const body = await c.req.json<{ name: string }>()
return c.json({ id: '123', name: body.name }, 201)
})
export type AppType = typeof app
export default appts// client.ts
import { hc } from 'hono/client'
import type { AppType } from './server'
const client = hc<AppType>('http://localhost:3000')
// ✅ Tự động suy kiểu response
const res = await client.users[':id'].$get({
param: { id: '123' },
})
const data = await res.json()
// data: { id: string; name: string; email: string }
// ✅ Input validated by type
const res2 = await client.users.$post({
json: { name: 'Bob' },
})tsKhông codegen, không schema riêng — TypeScript inference thuần.
Streaming & SSE#
// Server-Sent Events
app.get('/events', (c) => {
return c.stream(async (stream) => {
for (let i = 0; i < 10; i++) {
await stream.write(`data: Event ${i}\n\n`)
await stream.sleep(1000)
}
})
})
// Streaming response
app.get('/stream', (c) => {
return c.stream(async (stream) => {
for await (const chunk of readLargeFile()) {
await stream.write(chunk)
}
})
})tsTesting#
import { Hono } from 'hono'
import { test, describe, expect } from 'bun:test'
const app = new Hono()
app.get('/hello', (c) => c.json({ message: 'Hello!' }))
describe('GET /hello', () => {
test('should return 200', async () => {
const res = await app.request('/hello')
expect(res.status).toBe(200)
expect(await res.json()).toEqual({ message: 'Hello!' })
})
})tsKhông cần supertest — app.request() mock HTTP luôn.
Deploy#
Cloudflare Workers#
// wrangler.jsonc
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2026-07-01"
}tsnpx wrangler deploybashBun#
import { Hono } from 'hono'
import { serve } from 'bun'
const app = new Hono()
app.get('/', (c) => c.text('Bun + Hono'))
serve(app.fetch)tsDeno#
import { Hono } from 'jsr:@hono/hono'
const app = new Hono()
app.get('/', (c) => c.text('Deno + Hono'))
Deno.serve(app.fetch)tsNode.js#
npm install @hono/node-serverbashimport { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Node + Hono'))
serve(app, { port: 3000 })tsKhi Nào Dùng Hono?#
✅ Nên dùng#
- Edge API — Cloudflare Workers, Deno Deploy
- Serverless — cold start quan trọng
- Microservices nhẹ — muốn framework tối giản
- Multi-runtime — code chạy cả Workers + Node + Bun
- API nhanh — cần throughput cao
❌ Không nên#
- Full-featured web app — cần session, template engine, ORM tích hợp (chọn NextJS/Nuxt)
- Legacy Express project — migration không đáng
- Cần nhiều plugin cộng đồng — Hono còn mới, ecosystem nhỏ hơn Express/Fastify
So Sánh Chi Tiết#
Hono vs Express#
// Express
const express = require('express')
const app = express()
app.get('/api/users/:id', async (req, res) => {
const user = await db.findUser(req.params.id)
res.json(user)
})
// Hono (giống, nhưng nhẹ hơn, edge-native)
import { Hono } from 'hono'
const app = new Hono()
app.get('/api/users/:id', async (c) => {
const user = await db.findUser(c.req.param('id'))
return c.json(user)
})tsHono API quen thuộc nếu bạn biết Express, nhưng được thiết kế cho edge: không dùng Node API, dùng Web API chuẩn (Request/Response).
Hono vs Fastify#
// Fastify — plugin system mạnh, schema-based validation
fastify.post('/users', {
schema: {
body: {
type: 'object',
properties: { name: { type: 'string' } }
}
}
}, handler)
// Hono — Zod trực tiếp, không cần schema language
app.post('/users', zValidator('json', z.object({
name: z.string()
})), handler)tsFastify dùng JSON Schema (Ajv). Hono dùng Zod hoặc custom parser — linh hoạt hơn, TypeScript-native hơn.
Helper phổ biến#
const app = new Hono()
// Redirect
app.get('/old-path', (c) => c.redirect('/new-path', 301))
// Set cookie
app.get('/login', (c) => {
c.header('Set-Cookie', 'session=token; HttpOnly; Secure')
return c.text('Logged in')
})
// Custom status
app.get('/created', (c) => c.json({ id: 1 }, 201))
// Conditional response
app.get('/not-modified', (c) => c.body(null, 304))
// HTML response
app.get('/page', (c) => c.html(`
<!DOCTYPE html>
<h1>Hello Hono</h1>
`))tsTổng Kết#
Hono là web framework phù hợp nhất cho kỷ nguyên edge computing hiện tại.
| Ưu điểm | Nhược điểm |
|---|---|
| ~14KB — siêu nhẹ | Ecosystem còn nhỏ |
| Nhanh nhất (Trie router) | Chưa có nhiều template engine |
| Chạy mọi runtime | Không ORM tích hợp |
| TypeScript-first | |
| RPC type-safe built-in | |
| Cold start nhanh |
# Chạy app Hono đầu tiên trong 30s
npm create cloudflare@latest my-api -- --template hono
cd my-api
npm run devbashHono viết tắt của “Honestly Only Needs One” — đúng nghĩa: một framework cho mọi runtime.