본문으로 건너뛰기
Previous
Next
Hono Framework Guide | Ultra-Fast Edge Web Framework

Hono Framework Guide | Ultra-Fast Edge Web Framework

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

HonoExpressFastify
RuntimeAny (edge + Node)Node.jsNode.js
SpeedFastestSlowestFast
Bundle size~14KB~57KB~180KB
TypeScriptFirst-classCommunity typesBuilt-in
Edge supportNativeVia adapterLimited
EcosystemGrowingMatureMature

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-validator for type-safe request parsing
  • Cloudflare Workers: D1 (SQLite), KV, Secrets via wrangler.toml bindings
  • 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 ?�으�?검?�하?�면 ??글???��????�니??