# RPC 客户端

URL: https://caijiao.org/hono/03-typescript/03-rpc-client
Source: docs/hono/03-typescript/03-rpc-client.md
Description: 学习 Hono RPC 的基本思路，了解如何导出 AppType 并在客户端通过 hc 获得类型提示。

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。
