Skip to main content
Version: 10.x

HTTP RPC 规范

方法 <-> 类型映射

¥Methods <-> Type mapping

HTTP 方法测绘注意
GET.query()在查询参数中输入 JSON 字符串。
例如 myQuery?input=${encodeURIComponent(JSON.stringify(input))}
POST.mutation()作为 POST 正文输入。
不适用.subscription()HTTP 传输不支持订阅

访问嵌套过程

¥Accessing nested procedures

嵌套过程由点分隔,因此下面对 byId 的请求最终将成为对 /api/trpc/post.byId 的请求。

¥Nested procedures are separated by dots, so for a request to byId below would end up being a request to /api/trpc/post.byId.

ts
export const appRouter = router({
post: router({
byId: publicProcedure.input(String).query(async (opts) => {
// [...]
}),
}),
});
ts
export const appRouter = router({
post: router({
byId: publicProcedure.input(String).query(async (opts) => {
// [...]
}),
}),
});

批量

¥Batching

批处理时,我们使用数据加载器将同一 HTTP 方法的所有并行过程调用合并到一个请求中。

¥When batching, we combine all parallel procedure calls of the same HTTP method in one request using a data loader.

  • 被调用过程的名称由逗号 (,) 组合在 pathname

    ¥The called procedures' names are combined by a comma (,) in the pathname

  • 输入参数作为名为 input 的查询参数发送,其形状为 Record<number, unknown>

    ¥Input parameters are sent as a query parameter called input which has the shape Record<number, unknown>.

  • 我们还需要传递 batch=1 作为查询参数。

    ¥We also need to pass batch=1 as a query parameter.

  • 如果响应有不同的状态,我们发回 207 Multi-Status (例如,如果一个调用出错而一个调用成功)

    ¥If the response has different statuses, we send back 207 Multi-Status (e.g., if one call errored and one succeeded)

批量请求示例

¥Batching Example Request

给定一个在 /api/trpc 暴露的路由:

¥Given a router like this exposed at /api/trpc:

server/router.ts
tsx
export const appRouter = t.router({
postById: t.procedure.input(String).query(async (opts) => {
const post = await opts.ctx.post.findUnique({
where: { id: opts.input },
});
return post;
}),
relatedPosts: t.procedure.input(String).query(async (opts) => {
const posts = await opts.ctx.findRelatedPostsById(opts.input);
return posts;
}),
});
server/router.ts
tsx
export const appRouter = t.router({
postById: t.procedure.input(String).query(async (opts) => {
const post = await opts.ctx.post.findUnique({
where: { id: opts.input },
});
return post;
}),
relatedPosts: t.procedure.input(String).query(async (opts) => {
const posts = await opts.ctx.findRelatedPostsById(opts.input);
return posts;
}),
});

...在 React 组件中定义了两个这样的查询:

¥... And two queries defined like this in a React component:

MyComponent.tsx
tsx
export function MyComponent() {
const post1 = trpc.postById.useQuery('1');
const relatedPosts = trpc.relatedPosts.useQuery('1');
return (
<pre>
{JSON.stringify(
{
post1: post1.data ?? null,
relatedPosts: relatedPosts.data ?? null,
},
null,
4,
)}
</pre>
);
}
MyComponent.tsx
tsx
export function MyComponent() {
const post1 = trpc.postById.useQuery('1');
const relatedPosts = trpc.relatedPosts.useQuery('1');
return (
<pre>
{JSON.stringify(
{
post1: post1.data ?? null,
relatedPosts: relatedPosts.data ?? null,
},
null,
4,
)}
</pre>
);
}

以上将导致使用此数据进行 1 个 HTTP 调用:

¥The above would result in exactly 1 HTTP call with this data:

位置属性
pathname/api/trpc/postById,relatedPosts
search?batch=1&input=%7B%220%22%3A%221%22%2C%221%22%3A%221%22%7D *

*) 上面的 input 是以下结果:

¥*) input in the above is the result of:

ts
encodeURIComponent(
JSON.stringify({
0: '1', // <-- input for `postById`
1: '1', // <-- input for `relatedPosts`
}),
);
ts
encodeURIComponent(
JSON.stringify({
0: '1', // <-- input for `postById`
1: '1', // <-- input for `relatedPosts`
}),
);

批处理示例响应

¥Batching Example Response

Example output from server
json
[
// result for `postById`
{
"result": {
"data": {
"id": "1",
"title": "Hello tRPC",
"body": "..."
// ...
}
}
},
// result for `relatedPosts`
{
"result": {
"data": [
/* ... */
]
}
}
]
json
[
// result for `postById`
{
"result": {
"data": {
"id": "1",
"title": "Hello tRPC",
"body": "..."
// ...
}
}
},
// result for `relatedPosts`
{
"result": {
"data": [
/* ... */
]
}
}
]

HTTP 响应规范

¥HTTP Response Specification

为了拥有一个无论传输层如何都能工作的规范,我们尝试尽可能遵守 JSON-RPC 2.0

¥In order to have a specification that works regardless of the transport layer we try to conform to JSON-RPC 2.0 where possible.

成功回应

¥Successful Response

Example JSON Response
json
{
"result": {
"data": {
"id": "1",
"title": "Hello tRPC",
"body": "..."
}
}
}
json
{
"result": {
"data": {
"id": "1",
"title": "Hello tRPC",
"body": "..."
}
}
}
ts
{
result: {
data: TOutput; // output from procedure
}
}
ts
{
result: {
data: TOutput; // output from procedure
}
}

错误响应

¥Error Response

Example JSON Response
json
[
{
"error": {
"json": {
"message": "Something went wrong",
"code": -32600, // JSON-RPC 2.0 code
"data": {
// Extra, customizable, meta data
"code": "INTERNAL_SERVER_ERROR",
"httpStatus": 500,
"stack": "...",
"path": "post.add"
}
}
}
}
]
json
[
{
"error": {
"json": {
"message": "Something went wrong",
"code": -32600, // JSON-RPC 2.0 code
"data": {
// Extra, customizable, meta data
"code": "INTERNAL_SERVER_ERROR",
"httpStatus": 500,
"stack": "...",
"path": "post.add"
}
}
}
}
]

  • 如果可能,我们会传播抛出的错误中的 HTTP 状态代码。

    ¥When possible, we propagate HTTP status codes from the error thrown.

  • 如果响应有不同的状态,我们发回 207 Multi-Status (例如,如果一个调用出错而一个调用成功)

    ¥If the response has different statuses, we send back 207 Multi-Status (e.g., if one call errored and one succeeded)

  • 有关错误以及如何自定义错误的更多信息,请参阅 格式错误

    ¥For more on errors and how to customize them see Error Formatting.

错误代码 <-> HTTP 状态

¥Error Codes <-> HTTP Status

ts
PARSE_ERROR: 400,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
NOT_FOUND: 404,
FORBIDDEN: 403,
METHOD_NOT_SUPPORTED: 405,
TIMEOUT: 408,
CONFLICT: 409,
PRECONDITION_FAILED: 412,
PAYLOAD_TOO_LARGE: 413,
UNPROCESSABLE_CONTENT: 422,
TOO_MANY_REQUESTS: 429,
CLIENT_CLOSED_REQUEST: 499,
INTERNAL_SERVER_ERROR: 500,
NOT_IMPLEMENTED: 501,
ts
PARSE_ERROR: 400,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
NOT_FOUND: 404,
FORBIDDEN: 403,
METHOD_NOT_SUPPORTED: 405,
TIMEOUT: 408,
CONFLICT: 409,
PRECONDITION_FAILED: 412,
PAYLOAD_TOO_LARGE: 413,
UNPROCESSABLE_CONTENT: 422,
TOO_MANY_REQUESTS: 429,
CLIENT_CLOSED_REQUEST: 499,
INTERNAL_SERVER_ERROR: 500,
NOT_IMPLEMENTED: 501,

错误代码 <-> JSON-RPC 2.0 错误代码

¥Error Codes <-> JSON-RPC 2.0 Error Codes

Available codes & JSON-RPC code
ts
/**
* JSON-RPC 2.0 Error codes
* * `-32000` to `-32099` are reserved for implementation-defined server-errors.
* For tRPC we're copying the last digits of HTTP 4XX errors.
*/
export const TRPC_ERROR_CODES_BY_KEY = {
/**
* Invalid JSON was received by the server.
* An error occurred on the server while parsing the JSON text.
*/
PARSE_ERROR: -32700,
/**
* The JSON sent is not a valid Request object.
*/
BAD_REQUEST: -32600, // 400
// Internal JSON-RPC error
INTERNAL_SERVER_ERROR: -32603,
NOT_IMPLEMENTED: -32603,
// Implementation specific errors
UNAUTHORIZED: -32001, // 401
FORBIDDEN: -32003, // 403
NOT_FOUND: -32004, // 404
METHOD_NOT_SUPPORTED: -32005, // 405
TIMEOUT: -32008, // 408
CONFLICT: -32009, // 409
PRECONDITION_FAILED: -32012, // 412
PAYLOAD_TOO_LARGE: -32013, // 413
UNPROCESSABLE_CONTENT: -32022, // 422
TOO_MANY_REQUESTS: -32029, // 429
CLIENT_CLOSED_REQUEST: -32099, // 499
} as const;
ts
/**
* JSON-RPC 2.0 Error codes
* * `-32000` to `-32099` are reserved for implementation-defined server-errors.
* For tRPC we're copying the last digits of HTTP 4XX errors.
*/
export const TRPC_ERROR_CODES_BY_KEY = {
/**
* Invalid JSON was received by the server.
* An error occurred on the server while parsing the JSON text.
*/
PARSE_ERROR: -32700,
/**
* The JSON sent is not a valid Request object.
*/
BAD_REQUEST: -32600, // 400
// Internal JSON-RPC error
INTERNAL_SERVER_ERROR: -32603,
NOT_IMPLEMENTED: -32603,
// Implementation specific errors
UNAUTHORIZED: -32001, // 401
FORBIDDEN: -32003, // 403
NOT_FOUND: -32004, // 404
METHOD_NOT_SUPPORTED: -32005, // 405
TIMEOUT: -32008, // 408
CONFLICT: -32009, // 409
PRECONDITION_FAILED: -32012, // 412
PAYLOAD_TOO_LARGE: -32013, // 413
UNPROCESSABLE_CONTENT: -32022, // 422
TOO_MANY_REQUESTS: -32029, // 429
CLIENT_CLOSED_REQUEST: -32099, // 499
} as const;

深入挖掘

¥Dig deeper

你可以通过深入研究 TypeScript 定义来阅读更多详细信息

¥You can read more details by drilling into the TypeScript definitions in