Skip to content

Hono + tRPC:类型安全的端到端 API

使用 @hono/zod-openapi 生成 OpenAPI 文档

在现代全栈开发中,类型安全API 可视化是提升开发效率与协作质量的关键。Hono 与 tRPC 的理念高度契合 —— 都追求 端到端的类型安全(end-to-end typesafety),无需手动定义 DTO 或维护 Swagger 注解。

本文将展示如何结合 HonotRPC-like 类型模式,并使用 @hono/zod-openapi 自动生成 OpenAPI (Swagger) 文档,实现既类型安全又可文档化的 API 服务。

一、为什么 Hono + tRPC 是理想组合?

特性说明
类型安全前端调用 API 时自动获得类型提示
无 GraphQL / REST 模板使用函数式调用,类似 tRPC
轻量高效Hono ~2KB,适合边缘部署
自动生成 OpenAPI使用 Zod 定义输入输出,一键生成文档
开发体验佳TypeScript 全链路支持

注意:tRPC 原生不直接支持 Hono,但我们可以模仿 tRPC 的类型模式,并结合 Hono 的路由与中间件,实现类似体验。

二、核心架构:类型即契约

+----------------+       +------------------+       +-----------------+
|   Frontend     | ----> |   Hono API Route  | <---- |   Zod Schema    |
| (TypeScript)   |       | (with @hono/trpc) |       | (Input/Output)  |
+----------------+       +------------------+       +-----------------+
       |                                                   |
       +-------------------> @hono/zod-openapi <-----------+
                             生成 OpenAPI JSON

我们使用:

  • Zod 定义请求/响应结构;
  • @hono/zod-openapi 中间件生成 OpenAPI 文档;
  • 自定义 trpc() 工具实现 tRPC 风格的路由定义。

三、安装依赖

bash
npm install hono zod
npm install -D @hono/zod-openapi swagger-ui-react

或使用 Bun:

bash
bun add hono zod
bun add -D @hono/zod-openapi swagger-ui-react

四、定义 tRPC 风格的路由工厂

ts
// lib/trpc.ts
import { Hono } from 'hono'
import { z } from 'zod'

type ProcedureType = 'query' | 'mutation'

type RouterDef = {
  method: string
  path: string
  request: { params?: any; query?: any; body?: any }
  responses: { [code: string]: any }
}

type BuildRoute = (router: Hono) => void

export const createRouter = () => {
  const routers = new Hono()

  const createRoute = (type: ProcedureType) => {
    return {
      input: <T extends z.ZodTypeAny>(schema: T) => {
        return {
          output: <U extends z.ZodTypeAny>(outputSchema: U) => {
            return {
              handler: (handler: (input: { 
                input: z.infer<T>, 
                c: any 
              }) => Promise<z.infer<U>>) => {
                return { 
                  meta: { type, input: schema, output: outputSchema },
                  handler 
                }
              }
            }
          }
        }
      }
    }
  }

  const query = createRoute('query')
  const mutation = createRoute('mutation')

  return { routers, query, mutation }
}

五、定义类型安全的 API 路由

ts
// routes/user.route.ts
import { createRouter, query, mutation } from '../lib/trpc'
import { z } from 'zod'

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
})

const createUserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
})

export const userRouter = createRouter()

// GET /user/:id
userRouter.routers.get('/user/:id', 
  userRouter.query
    .input(z.object({ id: z.coerce.number() }))
    .output(userSchema)
    .handler(async ({ input, c }) => {
      // 模拟数据库查询
      return { id: input.id, name: 'John Doe', email: 'john@example.com' }
    })
)

// POST /user
userRouter.routers.post('/user',
  userRouter.mutation
    .input(createUserSchema)
    .output(userSchema)
    .handler(async ({ input, c }) => {
      // 模拟创建用户
      return { id: 123, ...input }
    })
)

export type UserRouter = typeof userRouter

六、使用 @hono/zod-openapi 生成 OpenAPI 文档

ts
// app.ts
import { Hono } from 'hono'
import { swaggerUI } from '@hono/swagger-ui'
import { OpenAPIHono } from '@hono/zod-openapi'

import { userRouter } from './routes/user.route'

const app = new OpenAPIHono()

// 注册路由
app.route('/api', userRouter.routers)

// 生成 OpenAPI 文档
app.doc('/doc', {
  info: {
    title: 'My API',
    version: '1.0.0',
    description: 'A tRPC-like API with OpenAPI docs'
  },
  openapi: '3.1.0',
  servers: [
    { url: 'http://localhost:3000' }
  ],
  tags: [
    { name: 'User', description: 'User management endpoints' }
  ]
})

// 提供 Swagger UI
app.get('/swagger', swaggerUI({ url: '/doc' }))

export default app

启动后访问:

  • http://localhost:3000/doc → OpenAPI JSON
  • http://localhost:3000/swagger → 可视化 Swagger UI

七、前端类型安全调用(类似 tRPC)

ts
// client/api.ts
import type { UserRouter } from '../server/routes/user.route'
import { z } from 'zod'

type InferRoute<T> = T extends { handler: () => infer U } ? U : never
type InferInput<T> = T extends { meta: { input: infer U } } ? z.infer<U> : never

export class ApiClient {
  private baseUrl: string

  constructor(baseUrl: string = '/api') {
    this.baseUrl = baseUrl
  }

  // GET /user/:id
  async getUser(id: number) {
    const res = await fetch(`${this.baseUrl}/user/${id}`)
    return await res.json() as z.infer<typeof userSchema>
  }

  // POST /user
  async createUser(data: InferInput<typeof userRouter>) {
    const res = await fetch(`${this.baseUrl}/user`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
    return await res.json()
  }
}

// 使用时自动获得类型提示
const api = new ApiClient()

const user = await api.getUser(1) // ✅ 类型自动推断
const newUser = await api.createUser({ name: 'Alice', email: 'alice@example.com' }) // ✅ 输入校验

八、自动注册 OpenAPI 元数据(进阶)

你可以封装一个装饰器或工厂函数,自动将路由信息注册到 OpenAPI:

ts
// lib/openapi.ts
import { z } from 'zod'
import { userSchema, createUserSchema } from '../routes/user.route'

export const userDocs = {
  getUser: {
    summary: 'Get user by ID',
    description: 'Returns a single user object',
    tags: ['User'],
    request: {
      params: z.object({ id: z.string() }),
    },
    responses: {
      200: {
        description: 'OK',
        content: {
          'application/json': {
            schema: userSchema
          }
        }
      }
    }
  }
}

然后在路由中使用 @hono/zod-openapiopenapi() 中间件:

ts
import { openapi } from '@hono/zod-openapi'

app.get('/user/:id', 
  openapi(userDocs.getUser),
  async (c) => {
    const id = Number(c.req.param('id'))
    return c.json({ id, name: 'John', email: 'john@example.com' })
  }
)

九、优势对比

方案类型安全OpenAPI性能学习成本
Express + Swagger❌ 手动维护
tRPC + Node.js❌(无 OpenAPI)
Hono + Zod + OpenAPI✅✅✅

Hono + Zod + @hono/zod-openapi = 类型安全 + 文档化 + 高性能

十、总结

通过结合 HonotRPC 风格的类型模式,并使用 @hono/zod-openapi,你可以:

目标实现方式
端到端类型安全使用 Zod 定义输入输出,前端自动推断
自动生成 OpenAPI@hono/zod-openapi 从 Zod Schema 生成文档
高性能路由Hono 轻量、快速,适合边缘部署
类似 tRPC 的体验函数式调用,无 REST 模板代码
可视化 API 文档集成 Swagger UI,便于调试和协作

这不是 tRPC,但它是 tRPC 的精神继承者 —— 更轻、更快、更开放
在 Hono 的世界里,你可以拥有 tRPC 的类型安全,同时获得 OpenAPI 的标准化和可视化能力。

立即尝试构建你的 类型安全、文档齐全、性能卓越 的下一代 API 服务!