Hono Framework Guide | Ultra-Fast Edge Web Framework
이 글의 핵심
Hono is a tiny, fast web framework built on Web Standards ??same code runs on Cloudflare Workers, Deno, Bun, and Node.js. This guide covers everything from routing to production JWT auth and edge database patterns.
Why Hono?
Hono is a lightweight, ultra-fast web framework built on Web Standards (Request/Response, Fetch API). It runs identically on:
- Cloudflare Workers ??edge, zero cold start
- Deno / Deno Deploy
- Bun
- Node.js (via adapter)
- AWS Lambda, Vercel Edge
The Radix Tree router delivers consistent performance at any scale. The entire framework is ~14KB gzipped.
Quick Start
# Cloudflare Workers project
npm create cloudflare@latest my-api -- --template hono
# Bun project
bun create hono my-api
# Node.js
npm install hono @hono/node-server
1. Routing
import { Hono } from 'hono'
const app = new Hono()
// Basic routes
app.get('/', (c) => c.text('Hello Hono!'))
app.post('/users', (c) => c.json({ created: true }))
app.put('/users/:id', (c) => c.json({ id: c.req.param('id') }))
app.delete('/users/:id', (c) => c.json({ deleted: true }))
// Route params
app.get('/users/:id', (c) => {
const id = c.req.param('id')
return c.json({ id })
})
// Query params
app.get('/search', (c) => {
const q = c.req.query('q')
const page = c.req.query('page') ?? '1'
return c.json({ q, page })
})
// Multiple params
app.get('/posts/:year/:month', (c) => {
const { year, month } = c.req.param()
return c.json({ year, month })
})
export default app
Route groups
// Group routes under a prefix
const api = new Hono().basePath('/api')
const users = new Hono()
users.get('/', (c) => c.json({ users: [] }))
users.get('/:id', (c) => c.json({ id: c.req.param('id') }))
users.post('/', async (c) => {
const body = await c.req.json()
return c.json({ created: body }, 201)
})
api.route('/users', users)
const app = new Hono()
app.route('/', api) // GET /api/users, GET /api/users/:id
export default app
2. Middleware
Hono middleware wraps request handling ??logging, auth, validation.
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { prettyJSON } from 'hono/pretty-json'
const app = new Hono()
// Built-in middleware
app.use('*', logger())
app.use('*', prettyJSON())
app.use('/api/*', cors({
origin: ['https://myapp.com', 'https://staging.myapp.com'],
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
}))
// Custom middleware
app.use('*', async (c, next) => {
const start = Date.now()
await next()
const elapsed = Date.now() - start
c.res.headers.set('X-Response-Time', `${elapsed}ms`)
})
// Route-specific middleware
const authMiddleware = async (c: Context, next: Next) => {
const token = c.req.header('Authorization')?.replace('Bearer ', '')
if (!token) return c.json({ error: 'Unauthorized' }, 401)
// validate token...
await next()
}
app.get('/protected', authMiddleware, (c) => c.json({ data: 'secret' }))
Context ??passing data between middleware
import { createMiddleware } from 'hono/factory'
// Type-safe context variables
type Variables = {
userId: string
userRole: 'admin' | 'user'
}
const app = new Hono<{ Variables: Variables }>()
const auth = createMiddleware<{ Variables: Variables }>(async (c, next) => {
const token = c.req.header('Authorization')?.replace('Bearer ', '')
if (!token) return c.json({ error: 'Unauthorized' }, 401)
// Decode token (simplified)
const payload = decodeJWT(token)
c.set('userId', payload.sub)
c.set('userRole', payload.role)
await next()
})
app.get('/profile', auth, (c) => {
const userId = c.get('userId') // type: string
const role = c.get('userRole') // type: 'admin' | 'user'
return c.json({ userId, role })
})
3. JWT Authentication
npm install hono # jwt middleware is built-in
import { jwt } from 'hono/jwt'
import { sign, verify } from 'hono/jwt'
const JWT_SECRET = process.env.JWT_SECRET!
// Protect routes
app.use('/api/*', jwt({ secret: JWT_SECRET }))
// Login ??issue token
app.post('/auth/login', async (c) => {
const { email, password } = await c.req.json()
const user = await db.users.findUnique({ where: { email } })
if (!user || !await bcrypt.compare(password, user.passwordHash)) {
return c.json({ error: 'Invalid credentials' }, 401)
}
const token = await sign(
{ sub: user.id, role: user.role, exp: Math.floor(Date.now() / 1000) + 3600 },
JWT_SECRET
)
return c.json({ token })
})
// Access JWT payload
app.get('/api/me', jwt({ secret: JWT_SECRET }), (c) => {
const payload = c.get('jwtPayload')
return c.json({ userId: payload.sub, role: payload.role })
})
4. Request Validation
Hono’s Zod validator provides type-safe request parsing:
npm install @hono/zod-validator zod
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const createUserSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
role: z.enum(['admin', 'user']).default('user'),
})
const updateUserSchema = createUserSchema.partial()
const querySchema = z.object({
page: z.coerce.number().min(1).default(1),
limit: z.coerce.number().min(1).max(100).default(20),
search: z.string().optional(),
})
app.post(
'/users',
zValidator('json', createUserSchema),
async (c) => {
const body = c.req.valid('json') // fully typed
const user = await db.users.create({ data: body })
return c.json(user, 201)
}
)
app.get(
'/users',
zValidator('query', querySchema),
async (c) => {
const { page, limit, search } = c.req.valid('query')
const users = await db.users.findMany({
skip: (page - 1) * limit,
take: limit,
where: search ? { name: { contains: search } } : undefined,
})
return c.json({ users, page, limit })
}
)
5. Cloudflare Workers ??Full Setup
// src/index.ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { jwt } from 'hono/jwt'
// Cloudflare Workers bindings type
type Bindings = {
DB: D1Database // D1 SQLite
KV: KVNamespace // KV store
JWT_SECRET: string // Secret from wrangler.toml
}
const app = new Hono<{ Bindings: Bindings }>()
app.use('*', cors())
// D1 database query
app.get('/posts', async (c) => {
const { results } = await c.env.DB.prepare(
'SELECT * FROM posts WHERE published = 1 ORDER BY created_at DESC LIMIT 20'
).all()
return c.json({ posts: results })
})
app.get('/posts/:id', async (c) => {
const id = c.req.param('id')
const post = await c.env.DB.prepare(
'SELECT * FROM posts WHERE id = ?'
).bind(id).first()
if (!post) return c.json({ error: 'Not found' }, 404)
return c.json(post)
})
app.post('/posts', jwt({ secret: (c) => c.env.JWT_SECRET }), async (c) => {
const body = await c.req.json()
const { success } = await c.env.DB.prepare(
'INSERT INTO posts (title, content, created_at) VALUES (?, ?, ?)'
).bind(body.title, body.content, new Date().toISOString()).run()
return c.json({ success }, 201)
})
// KV cache
app.get('/config', async (c) => {
const cached = await c.env.KV.get('site-config', 'json')
if (cached) return c.json(cached)
const config = await fetchConfig()
await c.env.KV.put('site-config', JSON.stringify(config), { expirationTtl: 3600 })
return c.json(config)
})
export default app
# wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "your-database-id"
[[kv_namespaces]]
binding = "KV"
id = "your-kv-id"
[vars]
JWT_SECRET = "your-secret" # use wrangler secret put JWT_SECRET for production
# Deploy
wrangler deploy
# Run locally
wrangler dev
6. Error Handling
import { HTTPException } from 'hono/http-exception'
// Throw structured errors anywhere
app.get('/users/:id', async (c) => {
const user = await db.users.findUnique({ where: { id: c.req.param('id') } })
if (!user) {
throw new HTTPException(404, { message: 'User not found' })
}
return c.json(user)
})
// Global error handler
app.onError((err, c) => {
if (err instanceof HTTPException) {
return c.json({ error: err.message }, err.status)
}
console.error('Unhandled error:', err)
return c.json({ error: 'Internal server error' }, 500)
})
// 404 handler
app.notFound((c) => c.json({ error: 'Route not found' }, 404))
7. Streaming Responses
Ideal for AI/LLM output:
import { streamText } from 'hono/streaming'
app.post('/chat', async (c) => {
const { messages } = await c.req.json()
return streamText(c, async (stream) => {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages,
stream: true,
})
for await (const chunk of response) {
const content = chunk.choices[0]?.delta?.content
if (content) {
await stream.write(`data: ${JSON.stringify({ content })}\n\n`)
}
}
await stream.write('data: [DONE]\n\n')
})
})
8. Node.js Deployment
// server.ts
import { serve } from '@hono/node-server'
import app from './app'
serve({
fetch: app.fetch,
port: 3000,
}, (info) => {
console.log(`Server running on http://localhost:${info.port}`)
})
Hono vs Express vs Fastify
| Hono | Express | Fastify | |
|---|---|---|---|
| Runtime | Any (edge + Node) | Node.js | Node.js |
| Speed | Fastest | Slowest | Fast |
| Bundle size | ~14KB | ~57KB | ~180KB |
| TypeScript | First-class | Community types | Built-in |
| Edge support | Native | Via adapter | Limited |
| Ecosystem | Growing | Mature | Mature |
Key Takeaways
- Hono = Web Standards framework that runs everywhere ??Workers, Deno, Bun, Node
- Routing: Radix Tree-based, supports params, query, route groups
- Middleware: built-in logger, CORS, JWT, rate limiting ??plus custom middleware with typed context
- Validation:
@hono/zod-validatorfor type-safe request parsing - Cloudflare Workers: D1 (SQLite), KV, Secrets via
wrangler.tomlbindings - Error handling:
HTTPException+app.onError()for consistent responses - Streaming:
streamText()for AI response streaming
?�주 묻는 질문 (FAQ)
Q. ???�용???�무?�서 ?�제 ?�나??
A. Build APIs on Cloudflare Workers, Deno, and Bun with Hono. Covers routing, middleware, JWT auth, CORS, D1 database, and ???�무?�서????본문???�제?� ?�택 가?�드�?참고???�용?�면 ?�니??
Q. ?�행?�로 ?�으�?좋�? 글?�?
A. �?글 ?�단???�전 글 ?�는 관??글 링크�??�라가�??�서?��?배울 ???�습?�다. C++ ?�리�?목차?�서 ?�체 ?�름???�인?????�습?�다.
Q. ??깊이 공�??�려�?
A. cppreference?� ?�당 ?�이브러�?공식 문서�?참고?�세?? 글 말�???참고 ?�료 링크???�용?�면 좋습?�다.
같이 보면 좋�? 글 (?��? 링크)
??주제?� ?�결?�는 ?�른 글?�니??
- [Edge Computing & Serverless Guide | Cloudflare Workers](/en/blog/edge-computing-serverless-guide/
- [Prisma Complete Guide | Schema· Queries](/en/blog/prisma-complete-guide/
- [Docker & Kubernetes Beginner](/en/blog/docker-kubernetes-beginner-guide/
??글?�서 ?�루???�워??(관??검?�어)
Hono, Edge, API, Cloudflare, TypeScript, Backend ?�으�?검?�하?�면 ??글???��????�니??