全书目录

第三十四篇:Hono 完全指南 —— 边缘轻骑哨站宇宙

第八站:RPC,是 Hono 最容易让人上头的一点

2 分钟 531 字 第 644 / 962 个阅读单元

到这里,Hono 已经像一个很顺手的边缘 API 框架了。

但它真正让很多 TS 团队觉得“这玩意儿有意思”的地方,是 RPC 类型联动

你可以把它想成:

后方参谋部和前线轻骑,不是各画一张地图,而是共用同一张作战图。

#服务端

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

const createDispatchSchema = z.object({
  outpostId: z.string(),
  supply: z.string(),
})

export const app = new Hono()
  .post(
    '/dispatches',
    zValidator('json', createDispatchSchema),
    (c) => {
      const input = c.req.valid('json')

      return c.json(
        {
          id: 'dispatch-001',
          ...input,
          status: 'queued',
        },
        201
      )
    }
  )
  .get('/dispatches/:id', (c) => {
    const id = c.req.param('id')

    if (id !== 'dispatch-001') {
      return c.json({ error: 'not found' }, 404)
    }

    return c.json(
      {
        id,
        status: 'queued',
      },
      200
    )
  })

export type AppType = typeof app

#客户端

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

const client = hc<AppType>('https://api.software-city.example', {
  headers: {
    Authorization: 'Bearer TOKEN',
  },
})

const created = await client.dispatches.$post({
  json: {
    outpostId: 'north-1',
    supply: 'medkit',
  },
})

const detail = await client.dispatches[':id'].$get({
  param: {
    id: 'dispatch-001',
  },
})

if (detail.status === 200) {
  const data = await detail.json()
  console.log(data.status)
}

#这里爽在哪里

爽点不是“少写几个接口文档”,而是:

  • 路径结构是有类型的
  • param / query / json / form 这些输入是有类型的
  • c.json(..., 200)c.json(..., 404) 这些响应分支也能带类型

也就是说,类型图谱从服务端延伸到了客户端。

#一个 RPC 重要细节:状态码尽量显式

官方文档特别强调过,如果你想让客户端推断足够准,最好显式写:

ts
return c.json({ error: 'not found' }, 404)
return c.json({ post }, 200)

而不是全靠默认行为。

尤其是 404 这种分支,如果你直接 c.notFound(),默认情况下客户端不一定能得到理想推断。要么显式 c.json(..., 404),要么你额外扩展类型。

#Monorepo 细节

如果你在 monorepo 里共享 AppType,官方 RPC 文档也明确提醒:

客户端和服务端的 tsconfig.json 都要开 strict: true

否则你会以为地图共用了,实际上很多类型信息没真正穿过去。