教程 hono tutorial

RPC 客户端

Hono 的 RPC 功能可以把服务端路由类型共享给客户端。它不是传统意义上的独立 RPC 协议,而是基于 Hono 路由类型和客户端辅助函数生成类型安全的调用方式。

服务端导出 AppType

先在服务端写路由:

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

const app = new Hono()

const route = app.post(
  '/posts',
  zValidator(
    'json',
    z.object({
      title: z.string().min(1),
      body: z.string().min(1),
    }),
  ),
  async (c) => {
    const input = c.req.valid('json')
    return c.json({
      id: crypto.randomUUID(),
      ...input,
    })
  },
)

export type AppType = typeof route
export default app

关键点是导出 typeof route 或包含路由的应用类型。

客户端使用 hc

客户端安装 hono 后使用 hc:

ts
import { hc } from 'hono/client'
import type { AppType } from '../server'

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

const res = await client.posts.$post({
  json: {
    title: 'Hono RPC',
    body: '类型可以从服务端路由推导',
  },
})

if (res.ok) {
  const post = await res.json()
  console.log(post.id)
}

当验证器声明了输入结构,客户端调用时也能获得对应类型提示。

Monorepo 中的结构

一个常见结构:

text
apps/
├─ api/
│  └─ src/index.ts
└─ web/
   └─ src/api.ts
packages/
└─ shared/

客户端应该只导入服务端导出的类型,不要导入会执行服务端代码的运行时对象。可以单独放一个 app-type.ts:

ts
// apps/api/src/app-type.ts
import type { app } from './app'

export type AppType = typeof app

tsconfig 要点

官方 RPC 文档建议在客户端和服务端的 tsconfig.json 中开启严格模式:

json
{
  "compilerOptions": {
    "strict": true
  }
}

如果类型没有正确推导,先检查:

  • 客户端导入的是 type,不是运行时代码。
  • 服务端导出的类型覆盖了实际路由。
  • 客户端和服务端使用的 Hono 版本一致。
  • strict 没有关掉。

适用场景

Hono RPC 适合小到中型 TypeScript 全栈项目,尤其是同一个仓库里同时维护 API 和前端客户端的场景。

如果你的客户端很多、语言不统一,或者需要公开长期稳定协议,更适合同时提供 OpenAPI 文档或手写 SDK。