RPC
orvajs/rpc 让你基于服务端路由定义构建 typed client,而不需要再手写一层接口描述。
创建客户端
ts
import { createRPC } from 'orvajs/rpc';
import { app } from '../src/app';
const client = createRPC<typeof app>({
baseURL: 'https://api.example.com',
headers: {
'x-client': 'orva-dashboard',
},
});js
import { createRPC } from "orvajs/rpc";
const client = createRPC({
baseURL: "https://api.example.com",
headers: {
"x-client": "orva-dashboard"
}
});调用方式
ts
const user = await client.api.users[':id'].$get({
param: { id: '42' },
query: { expand: 'posts' },
});HTTP 方法通过 $get、$post、$put、$delete、$patch、$options、$head 这类属性暴露。
c.json() 的响应类型推导
如果服务端路由直接返回 c.json(...),RPC 客户端的响应体类型会自动推导出来:
ts
const app = createOrva()
.get('/posts', (c) => c.json([{ id: 1, title: 'Post 1' }]))
.get('/posts/:id', (c) => c.json({ id: c.params.id, title: 'Post details' }));
const rpc = createRPC<typeof app>({
baseURL: 'https://api.example.com',
});
const posts = await rpc.posts.$get();
const list = await posts.json();
const post = await rpc.posts[':id'].$get({
param: { id: '123' },
});
const detail = await post.json();这里:
list的类型是{ id: number; title: string }[]detail的类型是{ id: string; title: string }
validator 输出到 RPC 入参推导
validator 的输出也会流入 RPC 请求参数类型:
ts
import { createOrva } from 'orvajs';
import { createRPC } from 'orvajs/rpc';
import { validator } from 'orvajs/validator';
const app = createOrva().post(
'/users',
validator('json', (value: any) => ({
name: String(value.name ?? ''),
age: Number(value.age ?? 0),
})),
(c) => c.json({ ok: true, user: c.valid('json') }),
);
const rpc = createRPC<typeof app>({
baseURL: 'https://api.example.com',
});
await rpc.users.$post({
body: {
name: 'Ada',
age: 20,
},
});这里客户端的 body 会被推导成 { name: string; age: number }。
请求选项
ts
await client.api.users.$post({
body: { name: 'Ada' },
headers: { Authorization: 'Bearer token' },
cookie: { session: 'abc' },
});支持的字段:
paramquerybodyheaderscookie
Content-Type 与 body 序列化
RPC 客户端会根据 Content-Type 决定如何序列化请求体:
- 未显式指定时:默认 JSON
application/x-www-form-urlencoded:转为URLSearchParamstext/*:转为字符串FormData:直接透传Blob/ArrayBuffer/Uint8Array/ReadableStream:直接透传
ts
await client.api.forms.submit.$post({
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: { name: 'Ada', role: 'admin' },
});错误处理
RPC 客户端默认不会在 !response.ok 时自动抛错,而是仍然返回一个带类型方法的响应对象。你可以根据 response.ok 或 response.status 自己决定如何处理。
ts
const response = await client.api.users.$post({ body: {} });
if (!response.ok) {
console.error(response.status, await response.text());
}元数据读取
如果你需要把路由契约暴露给前端工具链、代码生成器或平台治理层,可以读取 RPC 元数据:
ts
import { createRPCMetadata } from 'orvajs/rpc';
const routes = createRPCMetadata(app);js
import { createRPCMetadata } from "orvajs/rpc";
const routes = createRPCMetadata(app);它会返回每条路由的:
methodpathvalidatorsresponseSchemas
最佳实践
- 把
typeof app作为客户端泛型来源,避免重复声明接口。 - 路由拆分优先用
route()或返回分组 app,以便保留类型注册表。 - 将 validator 与 OpenAPI 一起接入,RPC 类型会更完整。