HTTP RPC 规范
非官方测试版翻译
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
方法与类型映射
| HTTP Method | Mapping | Notes |
|---|---|---|
GET | .query() | Input JSON-stringified in query param. e.g. myQuery?input=${encodeURIComponent(JSON.stringify(input))} |
POST | .mutation() | Input as POST body. |
| n/a | .subscription() | Subscriptions are not supported in HTTP transport |
访问嵌套过程
嵌套过程使用点号分隔,因此访问下方 byId 的请求最终会映射到 /api/trpc/post.byId 端点。
tsexport const appRouter = router({post: router({byId: publicProcedure.input(String).query(async (opts) => {// [...]}),}),});
tsexport const appRouter = router({post: router({byId: publicProcedure.input(String).query(async (opts) => {// [...]}),}),});
批量处理
进行批处理时,我们会使用数据加载器将相同 HTTP 方法的所有并行过程调用合并为单个请求。
-
被调用过程名称通过逗号 (
,) 组合在pathname中 -
输入参数通过名为
input的查询参数传递,其结构为Record<number, unknown> -
需要额外传递
batch=1查询参数 -
当响应包含不同状态码时,返回
207 Multi-Status(例如某个调用失败而另一个成功时)
批量处理请求示例
假设在 /api/trpc 暴露的路由如下:
server/router.tstsxexport 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.tstsxexport 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 组件中定义的两个查询如下:
MyComponent.tsxtsxexport 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.tsxtsxexport 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>);}
上述操作将产生单次 HTTP 调用,数据如下:
| Location property | Value |
|---|---|
pathname | /api/trpc/postById,relatedPosts |
search | ?batch=1&input=%7B%220%22%3A%221%22%2C%221%22%3A%221%22%7D * |
*) 上图中的 input 是以下操作的结果:
tsencodeURIComponent(JSON.stringify({0: '1', // <-- input for `postById`1: '1', // <-- input for `relatedPosts`}),);
tsencodeURIComponent(JSON.stringify({0: '1', // <-- input for `postById`1: '1', // <-- input for `relatedPosts`}),);
批量处理响应示例
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 响应规范
为确保规范不受传输层限制,我们尽可能遵循 JSON-RPC 2.0 标准。
成功响应
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}}
错误响应
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 状态码
-
当响应包含不同状态码时,返回
207 Multi-Status(例如某个调用失败而另一个成功时) -
关于错误处理及自定义方法,详见错误格式化
错误代码与 HTTP 状态映射
tsPARSE_ERROR: 400,BAD_REQUEST: 400,UNAUTHORIZED: 401,FORBIDDEN: 403,NOT_FOUND: 404,METHOD_NOT_SUPPORTED: 405,TIMEOUT: 408,CONFLICT: 409,PRECONDITION_FAILED: 412,PAYLOAD_TOO_LARGE: 413,UNSUPPORTED_MEDIA_TYPE: 415,UNPROCESSABLE_CONTENT: 422,PRECONDITION_REQUIRED: 428,TOO_MANY_REQUESTS: 429,CLIENT_CLOSED_REQUEST: 499,INTERNAL_SERVER_ERROR: 500,NOT_IMPLEMENTED: 501,BAD_GATEWAY: 502,SERVICE_UNAVAILABLE: 503,GATEWAY_TIMEOUT: 504,
tsPARSE_ERROR: 400,BAD_REQUEST: 400,UNAUTHORIZED: 401,FORBIDDEN: 403,NOT_FOUND: 404,METHOD_NOT_SUPPORTED: 405,TIMEOUT: 408,CONFLICT: 409,PRECONDITION_FAILED: 412,PAYLOAD_TOO_LARGE: 413,UNSUPPORTED_MEDIA_TYPE: 415,UNPROCESSABLE_CONTENT: 422,PRECONDITION_REQUIRED: 428,TOO_MANY_REQUESTS: 429,CLIENT_CLOSED_REQUEST: 499,INTERNAL_SERVER_ERROR: 500,NOT_IMPLEMENTED: 501,BAD_GATEWAY: 502,SERVICE_UNAVAILABLE: 503,GATEWAY_TIMEOUT: 504,
错误代码与 JSON-RPC 2.0 映射
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 errorINTERNAL_SERVER_ERROR: -32603, // 500NOT_IMPLEMENTED: -32603, // 501BAD_GATEWAY: -32603, // 502SERVICE_UNAVAILABLE: -32603, // 503GATEWAY_TIMEOUT: -32603, // 504// Implementation specific errorsUNAUTHORIZED: -32001, // 401FORBIDDEN: -32003, // 403NOT_FOUND: -32004, // 404METHOD_NOT_SUPPORTED: -32005, // 405TIMEOUT: -32008, // 408CONFLICT: -32009, // 409PRECONDITION_FAILED: -32012, // 412PAYLOAD_TOO_LARGE: -32013, // 413UNSUPPORTED_MEDIA_TYPE: -32015, // 415UNPROCESSABLE_CONTENT: -32022, // 422PRECONDITION_REQUIRED: -32028, // 428TOO_MANY_REQUESTS: -32029, // 429CLIENT_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 errorINTERNAL_SERVER_ERROR: -32603, // 500NOT_IMPLEMENTED: -32603, // 501BAD_GATEWAY: -32603, // 502SERVICE_UNAVAILABLE: -32603, // 503GATEWAY_TIMEOUT: -32603, // 504// Implementation specific errorsUNAUTHORIZED: -32001, // 401FORBIDDEN: -32003, // 403NOT_FOUND: -32004, // 404METHOD_NOT_SUPPORTED: -32005, // 405TIMEOUT: -32008, // 408CONFLICT: -32009, // 409PRECONDITION_FAILED: -32012, // 412PAYLOAD_TOO_LARGE: -32013, // 413UNSUPPORTED_MEDIA_TYPE: -32015, // 415UNPROCESSABLE_CONTENT: -32022, // 422PRECONDITION_REQUIRED: -32028, // 428TOO_MANY_REQUESTS: -32029, // 429CLIENT_CLOSED_REQUEST: -32099, // 499} as const;
覆写默认 HTTP 方法
要覆写查询/变更的默认 HTTP 方法,可使用 methodOverride 选项:
server/httpHandler.tstsx// Your server must separately allow the client to override the HTTP methodconst handler = createHTTPHandler({router: router,allowMethodOverride: true,});
server/httpHandler.tstsx// Your server must separately allow the client to override the HTTP methodconst handler = createHTTPHandler({router: router,allowMethodOverride: true,});
client/trpc.tstsx// The client can then specify which HTTP method to use for all queries/mutationsconst client = createTRPCClient<AppRouter>({links: [httpLink({url: `http://localhost:3000`,methodOverride: 'POST', // all queries and mutations will be sent to the tRPC Server as POST requests.}),],});
client/trpc.tstsx// The client can then specify which HTTP method to use for all queries/mutationsconst client = createTRPCClient<AppRouter>({links: [httpLink({url: `http://localhost:3000`,methodOverride: 'POST', // all queries and mutations will be sent to the tRPC Server as POST requests.}),],});
深入探索
可通过以下 TypeScript 定义获取更多实现细节: