DevToolBox免费
博客

Hono 完全指南:超快边缘计算 Web 框架

20 min read作者 DevToolBox Team

TL;DR

Hono 是超快的多运行时 Web 框架(比 Express 快 2.5 倍),内置 TypeScript、中间件、Zod 验证、JWT 认证、CORS、OpenAPI 和 RPC 模式。

Key Takeaways

  • 无需修改代码即可在所有主流 JavaScript 运行时上运行。
  • 内置 JWT、CORS、ETag、日志和压缩中间件。
  • 基于 Zod 的验证提供运行时类型检查和自动 TypeScript 推断。
  • RPC 模式提供端到端类型安全,无需代码生成。
  • OpenAPI 集成自动生成 Swagger 文档。
  • 在 Bun 上每秒处理 130K+ 请求,性能超过 Express 2.5 倍。

Hono 是一个小巧、简洁、超快的 Web 框架,专为边缘计算设计。凭借一流的 TypeScript 支持和零依赖,Hono 几乎可以在所有 JavaScript 运行时上运行。

什么是 Hono?

Hono(日语"火焰")是 2022 年创建的轻量级 Web 框架,使用 Web 标准 API,无需 polyfill 即可跨运行时移植。

  • 超快:RegExpRouter 在 Bun 上达到 130K+ 请求/秒
  • 极小:核心约 14KB,零外部依赖
  • 多运行时:Cloudflare Workers、Deno、Bun、Node.js、AWS Lambda
  • 类型安全:一流 TypeScript 支持和 RPC 模式
  • 丰富中间件:内置 JWT、CORS、ETag、日志、压缩等
  • Web 标准:基于 Request/Response 构建

安装和设置

Hono 为每个运行时提供启动模板。

# Create a new Hono project
npm create hono@latest my-app

# Select your target runtime:
#   cloudflare-workers / deno / bun / nodejs / aws-lambda

cd my-app && npm install

# Or install manually
npm install hono

# Project structure
# my-app/
# ├── src/index.ts    # Entry point
# ├── package.json
# ├── tsconfig.json
# └── wrangler.toml   # (Cloudflare only)

路由

Hono 提供强大的路由系统,支持路径参数、通配符和正则约束。

基本路由

import { Hono } from 'hono'
const app = new Hono()

app.get('/', (c) => c.text('Hello Hono!'))

app.post('/users', async (c) => {
  const body = await c.req.json()
  return c.json({ id: 1, ...body }, 201)
})

app.put('/users/:id', async (c) => {
  const id = c.req.param('id')
  const body = await c.req.json()
  return c.json({ id, ...body })
})

app.delete('/users/:id', (c) => {
  return c.json({ deleted: c.req.param('id') })
})

app.on(['PUT', 'PATCH'], '/items/:id', (c) => {
  return c.json({ updated: true })
})

app.all('/api/*', (c) => c.json({ method: c.req.method }))

export default app

分组和嵌套路由

使用 app.route() 将路由组织成逻辑组。

import { Hono } from 'hono'

// routes/users.ts
const users = new Hono()
users.get('/', (c) => c.json([{ id: 1, name: 'Alice' }]))
users.get('/:id', (c) => c.json({ id: c.req.param('id') }))
users.post('/', async (c) => c.json(await c.req.json(), 201))

// routes/posts.ts
const posts = new Hono()
posts.get('/', (c) => c.json([{ id: 1, title: 'Hello' }]))
posts.get('/:id', (c) => c.json({ id: c.req.param('id') }))

// Main app — mount route groups
const app = new Hono()
app.route('/api/users', users)
app.route('/api/posts', posts)
export default app

路由参数和通配符

// Named parameters
app.get('/users/:id', (c) => c.json({ id: c.req.param('id') }))

// Multiple parameters
app.get('/orgs/:orgId/repos/:repoId', (c) => {
  const { orgId, repoId } = c.req.param()
  return c.json({ orgId, repoId })
})

// Optional parameter
app.get('/articles/:slug/:format?', (c) => {
  const format = c.req.param('format') || 'html'
  return c.json({ slug: c.req.param('slug'), format })
})

// Wildcard
app.get('/files/*', (c) => c.text('File: ' + c.req.path))

// Regex constraint
app.get('/posts/:id{[0-9]+}', (c) => {
  return c.json({ id: Number(c.req.param('id')) })
})

中间件

Hono 中间件遵循洋葱模型,内置丰富中间件且易于自定义。

内置中间件

import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { cors } from 'hono/cors'
import { etag } from 'hono/etag'
import { compress } from 'hono/compress'
import { secureHeaders } from 'hono/secure-headers'
import { basicAuth } from 'hono/basic-auth'
import { bearerAuth } from 'hono/bearer-auth'
import { prettyJSON } from 'hono/pretty-json'

const app = new Hono()
app.use('*', logger())           // Log method, path, status
app.use('/api/*', cors())         // Cross-origin requests
app.use('*', etag())             // Caching headers
app.use('*', compress())         // Gzip/brotli
app.use('*', secureHeaders())    // CSP, X-Frame-Options
app.use('*', prettyJSON())       // ?pretty for formatted output

app.use('/admin/*', basicAuth({
  username: 'admin', password: 'secret',
}))
app.use('/api/private/*', bearerAuth({
  token: 'my-secret-token',
}))

自定义中间件

import { createMiddleware } from 'hono/factory'

// Timing middleware
const timer = createMiddleware(async (c, next) => {
  const start = Date.now()
  await next()
  c.header('X-Response-Time', (Date.now() - start) + 'ms')
})

// Typed auth middleware
type Env = { Variables: { user: { id: string; role: string } } }

const auth = createMiddleware<Env>(async (c, next) => {
  const token = c.req.header('Authorization')
  if (!token) return c.json({ error: 'Unauthorized' }, 401)
  c.set('user', { id: '123', role: 'admin' })
  await next()
})

const app = new Hono<Env>()
app.use('*', timer)
app.use('/protected/*', auth)
app.get('/protected/profile', (c) => {
  const user = c.get('user')  // fully typed!
  return c.json({ user })
})

请求处理

Hono 上下文对象提供便捷方法访问请求数据。

app.post('/upload', async (c) => {
  // Headers
  const contentType = c.req.header('Content-Type')

  // Query parameters
  const page = c.req.query('page')       // single
  const tags = c.req.queries('tag')       // array

  // JSON body
  const json = await c.req.json()

  // Form data
  const form = await c.req.formData()
  const file = form.get('file') as File

  // URL info
  const url = c.req.url       // full URL
  const path = c.req.path     // pathname
  const method = c.req.method // HTTP method

  // Path parameters
  const id = c.req.param('id')

  // Raw body
  const text = await c.req.text()
  const buffer = await c.req.arrayBuffer()

  return c.json({ received: true })
})

响应辅助函数

Hono 提供 JSON、HTML、文本、重定向、流式等响应辅助函数。

// JSON, text, HTML responses
app.get('/json', (c) => c.json({ message: 'hello' }))
app.get('/error', (c) => c.json({ error: 'not found' }, 404))
app.get('/text', (c) => c.text('Hello World'))
app.get('/page', (c) => c.html('<h1>Hello</h1>'))

// Redirects
app.get('/old', (c) => c.redirect('/new'))
app.get('/moved', (c) => c.redirect('/permanent', 301))

// Headers and status
app.get('/custom', (c) => {
  c.header('X-Custom', 'value')
  c.header('Cache-Control', 'max-age=3600')
  c.status(201)
  return c.json({ created: true })
})

// Streaming
app.get('/stream', (c) => {
  return c.streamText(async (stream) => {
    for (let i = 0; i < 5; i++) {
      await stream.writeln('Chunk ' + i)
      await stream.sleep(1000)
    }
  })
})

Zod 验证

Hono 通过 @hono/zod-validator 与 Zod 集成。

import { z } from 'zod'
import { zValidator } from '@hono/zod-validator'

// Validate JSON body
const userSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).optional(),
})

app.post('/users', zValidator('json', userSchema), (c) => {
  const body = c.req.valid('json')  // fully typed
  return c.json({ user: body }, 201)
})

// Validate query parameters
const listSchema = z.object({
  page: z.coerce.number().int().min(1).default(1),
  limit: z.coerce.number().int().max(100).default(20),
  search: z.string().optional(),
})

app.get('/users', zValidator('query', listSchema), (c) => {
  const { page, limit, search } = c.req.valid('query')
  return c.json({ page, limit, search })
})

// Validate path params + custom error handling
app.get('/users/:id',
  zValidator('param', z.object({ id: z.coerce.number().positive() }),
    (result, c) => {
      if (!result.success) {
        return c.json({ errors: result.error.flatten() }, 422)
      }
    }
  ),
  (c) => c.json({ id: c.req.valid('param').id })
)

上下文对象

上下文对象 (c) 是每个 Hono 处理器的核心。

type AppEnv = {
  Bindings: { DATABASE_URL: string; API_KEY: string }
  Variables: { requestId: string }
}

const app = new Hono<AppEnv>()

app.use('*', async (c, next) => {
  c.set('requestId', crypto.randomUUID())
  await next()
})

app.get('/demo', (c) => {
  const url = c.req.url          // request URL
  const dbUrl = c.env.DATABASE_URL // env bindings
  const reqId = c.get('requestId') // typed variables
  c.header('X-Request-Id', reqId)  // set headers
  return c.json({ requestId: reqId, url })
})

// waitUntil (Cloudflare Workers)
app.post('/webhook', (c) => {
  c.executionCtx.waitUntil(
    fetch('https://analytics.example.com/track', {
      method: 'POST',
      body: JSON.stringify({ event: 'webhook' }),
    })
  )
  return c.json({ ok: true })
})

运行时适配器

Hono 的多运行时支持是其最大优势之一。

Cloudflare Workers

// src/index.ts — Cloudflare Workers
import { Hono } from 'hono'

type Bindings = {
  MY_KV: KVNamespace
  MY_DB: D1Database
  MY_BUCKET: R2Bucket
  API_KEY: string
}

const app = new Hono<{ Bindings: Bindings }>()

app.get('/kv/:key', async (c) => {
  const val = await c.env.MY_KV.get(c.req.param('key'))
  return val ? c.json({ val }) : c.json({ error: 'Not found' }, 404)
})

app.get('/db/users', async (c) => {
  const { results } = await c.env.MY_DB
    .prepare('SELECT * FROM users LIMIT 10')
    .all()
  return c.json(results)
})

export default app

Deno

// main.ts — Deno
import { Hono } from 'https://deno.land/x/hono/mod.ts'

const app = new Hono()
app.get('/', (c) => c.text('Hello from Deno!'))
app.get('/env', (c) => {
  return c.json({ runtime: 'deno', port: Deno.env.get('PORT') })
})

Deno.serve(app.fetch)
// Run: deno run --allow-net --allow-env main.ts

Bun

// index.ts — Bun (fastest runtime for Hono)
import { Hono } from 'hono'

const app = new Hono()
app.get('/', (c) => c.text('Hello from Bun!'))
app.get('/file', async (c) => {
  const data = await Bun.file('./data.json').json()
  return c.json(data)
})

export default { port: 3000, fetch: app.fetch }
// Run: bun run index.ts

Node.js

// index.ts — 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!'))
app.get('/health', (c) => {
  return c.json({ status: 'ok', version: process.version })
})

serve({ fetch: app.fetch, port: 3000 })
// Install: npm install hono @hono/node-server
// Run: npx tsx index.ts

AWS Lambda

// lambda.ts — AWS Lambda
import { Hono } from 'hono'
import { handle } from 'hono/aws-lambda'

const app = new Hono()
app.get('/', (c) => c.text('Hello from Lambda!'))
app.get('/items', (c) => {
  return c.json([{ id: 1, name: 'Item A' }])
})

// Export handler for API Gateway or Lambda URL
export const handler = handle(app)

错误处理

Hono 通过 app.onError() 和 HTTPException 类提供结构化错误处理。

import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'

const app = new Hono()

// Global error handler
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return c.json(
      { error: err.message, status: err.status },
      err.status
    )
  }
  console.error(err)
  return c.json({ error: 'Internal Server Error' }, 500)
})

// 404 handler
app.notFound((c) => {
  return c.json({ error: 'Not Found', path: c.req.path }, 404)
})

// Throw HTTPException in handlers
app.get('/users/:id', async (c) => {
  const user = await findUser(c.req.param('id'))
  if (!user) {
    throw new HTTPException(404, {
      message: 'User not found',
    })
  }
  return c.json(user)
})

// Custom error with response
app.get('/protected', (c) => {
  throw new HTTPException(401, {
    message: 'Authentication required',
    res: new Response('Unauthorized', {
      status: 401,
      headers: { 'WWW-Authenticate': 'Bearer' },
    }),
  })
})

测试

Hono 通过 app.request() 提供内置测试客户端。

// app.test.ts — using Vitest
import { describe, it, expect } from 'vitest'
import app from './app'

describe('API', () => {
  it('GET / returns OK', async () => {
    const res = await app.request('/')
    expect(res.status).toBe(200)
    expect(await res.text()).toBe('OK')
  })

  it('GET /users/:id returns user', async () => {
    const res = await app.request('/users/42')
    const data = await res.json()
    expect(data.id).toBe('42')
  })

  it('POST /users creates user', async () => {
    const res = await app.request('/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: 'Alice' }),
    })
    expect(res.status).toBe(201)
    const data = await res.json()
    expect(data.name).toBe('Alice')
  })

  it('returns 404 for unknown routes', async () => {
    const res = await app.request('/nonexistent')
    expect(res.status).toBe(404)
  })

  it('handles auth with Bearer token', async () => {
    const res = await app.request('/api/me', {
      headers: { Authorization: 'Bearer my-token' },
    })
    expect(res.status).toBe(200)
  })
})

JWT 认证

Hono 包含内置的 JWT 中间件用于令牌验证。

import { Hono } from 'hono'
import { jwt, sign } from 'hono/jwt'

const app = new Hono()
const SECRET = 'my-jwt-secret-key'

// Login — generate token
app.post('/auth/login', async (c) => {
  const { email, password } = await c.req.json()
  if (email !== 'admin@test.com') {
    return c.json({ error: 'Invalid credentials' }, 401)
  }
  const token = await sign({
    sub: 'user-123', email, role: 'admin',
    exp: Math.floor(Date.now() / 1000) + 3600,
  }, SECRET)
  return c.json({ token })
})

// Protect routes
app.use('/api/*', jwt({ secret: SECRET }))

app.get('/api/me', (c) => {
  const payload = c.get('jwtPayload')
  return c.json({ userId: payload.sub, role: payload.role })
})

// Role-based access control
const requireRole = (role: string) => async (c: any, next: any) => {
  if (c.get('jwtPayload').role !== role) {
    return c.json({ error: 'Forbidden' }, 403)
  }
  await next()
}

app.delete('/api/admin/users/:id', requireRole('admin'),
  (c) => c.json({ deleted: c.req.param('id') })
)

CORS 配置

内置 CORS 中间件处理跨域请求。

import { cors } from 'hono/cors'

// Allow all origins (development)
app.use('/api/*', cors())

// Restrict to specific origins (production)
app.use('/api/*', cors({
  origin: ['https://myapp.com', 'https://staging.myapp.com'],
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowHeaders: ['Content-Type', 'Authorization'],
  exposeHeaders: ['X-Total-Count'],
  maxAge: 86400,
  credentials: true,
}))

// Dynamic origin
app.use('/api/*', cors({
  origin: (origin) => {
    return origin.endsWith('.myapp.com') ? origin : 'https://myapp.com'
  },
}))

OpenAPI 集成

@hono/zod-openapi 自动生成 Swagger 文档。

import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'
import { swaggerUI } from '@hono/swagger-ui'

const app = new OpenAPIHono()

const getUserRoute = createRoute({
  method: 'get',
  path: '/users/{id}',
  request: {
    params: z.object({
      id: z.string().openapi({ example: '123' }),
    }),
  },
  responses: {
    200: {
      content: { 'application/json': {
        schema: z.object({
          id: z.string(), name: z.string(), email: z.string().email(),
        }),
      }},
      description: 'User found',
    },
  },
})

app.openapi(getUserRoute, (c) => {
  const { id } = c.req.valid('param')
  return c.json({ id, name: 'Alice', email: 'a@b.com' })
})

// Serve OpenAPI spec + Swagger UI
app.doc('/doc', {
  openapi: '3.0.0',
  info: { title: 'My API', version: '1.0.0' },
})
app.get('/ui', swaggerUI({ url: '/doc' }))

RPC 模式

RPC 模式提供端到端类型安全,无需代码生成。

// ---- Server (server.ts) ----
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()
  .get('/api/posts', (c) => {
    return c.json([{ id: 1, title: 'Hello Hono' }])
  })
  .post('/api/posts',
    zValidator('json', z.object({
      title: z.string(), content: z.string(),
    })),
    (c) => c.json({ id: 2, ...c.req.valid('json') }, 201)
  )

export type AppType = typeof app
export default app

// ---- Client (client.ts) ----
import { hc } from 'hono/client'
import type { AppType } from './server'

const client = hc<AppType>('http://localhost:3000')

// Fully typed — IDE autocomplete + compile-time checks
const res = await client.api.posts.$get()
const data = await res.json() // typed as { id: number; title: string }[]

const newPost = await client.api.posts.$post({
  json: { title: 'New', content: 'Hello!' },
}) // TypeScript error if shape is wrong

性能基准

Hono 在基准测试中持续超越 Express、Fastify 和 Koa。

Benchmark Results (simple JSON, 10s)

  • Hono (Bun): ~130,000 请求/秒
  • Fastify (Node.js): ~75,000 请求/秒
  • Express (Node.js): ~50,000 请求/秒
  • Hono (Deno): ~95,000 请求/秒
  • Hono (Workers): 亚毫秒冷启动,~80,000 请求/秒
// Performance tips
// 1. RegExpRouter (default) — fastest for static routes
const app = new Hono()  // uses RegExpRouter

// 2. LinearRouter — better for dynamic route registration
import { LinearRouter } from 'hono/router/linear-router'
const app = new Hono({ router: new LinearRouter() })

// 3. Use c.json() over new Response()
app.get('/fast', (c) => c.json({ ok: true }))

// 4. Stream large responses
app.get('/large', (c) => {
  return c.streamText(async (stream) => {
    for (const chunk of data) await stream.write(chunk)
  })
})

// 5. Cache with ETag
import { etag } from 'hono/etag'
app.use('/api/*', etag())

常见问题

Hono 是什么,与 Express 有何不同?

Hono 是基于 Web 标准 API 的超快轻量级框架,可在多个运行时运行,速度快 2-3 倍。

Hono 能在生产中替代 Express 吗?

可以。Hono 已生产就绪,被 Cloudflare、Vercel 等使用。

哪个运行时对 Hono 最快?

Bun 最高 130K+ 请求/秒,Deno 约 95K,Workers 延迟最低。

Hono 如何处理验证?

通过 @hono/zod-validator 与 Zod 集成,自动验证并推断类型。

Hono 支持 WebSocket 吗?

支持,通过 hono/websocket 在支持的运行时上使用。

如何部署 Hono 到 Cloudflare Workers?

用 "npm create hono@latest" 创建项目,选 cloudflare-workers,然后 "wrangler deploy"。

Hono RPC 模式是什么?

RPC 模式在服务器和客户端间提供端到端类型安全,无需代码生成。

Hono 能自动生成 OpenAPI 文档吗?

可以,@hono/zod-openapi 从路由定义自动生成 OpenAPI 3.0 规范。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON FormatterJSON Validator

相关文章

tRPC 完全指南:TypeScript 端到端类型安全 API

掌握 tRPC 路由、过程、Zod 验证、中间件、React Query 集成、订阅、错误处理与测试。

Deno 完全指南:安全的 JavaScript 运行时

掌握 Deno 运行时,包括安全权限、TypeScript 支持、标准库、HTTP 服务器、测试与 Deno Deploy。

Bun 完全指南:全能 JavaScript 运行时

掌握 Bun 运行时,包括包管理器、打包器、测试运行器、HTTP 服务器、SQLite 与 Shell 脚本。