HTTP RPC 規範
方法 <-> 型別對應
HTTP 方法 | 對應 | 備註 |
---|---|---|
GET | .query() | 輸入 JSON 字串化在查詢參數中。 例如 myQuery?input=${encodeURIComponent(JSON.stringify(input))} |
POST | .mutation() | 輸入為 POST 主體。 |
不適用 | .subscription() | HTTP 傳輸不支援訂閱 |
存取巢狀程序
巢狀程序以點分隔,因此對下方 byId
的請求最終會成為對 /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) => {// [...]}),}),});
批次處理
批次處理時,我們使用資料載入器,將同一個 HTTP 方法的所有平行程序呼叫合併成一個請求。
- 被呼叫的程序名稱會以逗號 (
,
) 結合在pathname
中。 - 輸入參數會作為一個稱為
input
的查詢參數傳送,其形狀為Record<number, unknown>
。 - 我們也需要將
batch=1
傳遞為查詢參數。 - 如果回應有不同的狀態,我們會傳回
207 Multi-Status
(例如,如果一個呼叫出錯,而另一個成功)
批次處理範例請求
假設有一個路由器像這樣公開在 /api/trpc
:
server/router.tstsx
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.tstsx
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 元件中定義的兩個查詢如下:
MyComponent.tsxtsx
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.tsxtsx
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>);}
上述內容將產生一個 HTTP 呼叫,其資料如下:
位置屬性 | 值 |
---|---|
pathname | /api/trpc/postById,relatedPosts |
search | ?batch=1&input=%7B%220%22%3A%221%22%2C%221%22%3A%221%22%7D * |
*) 上述的 input
是
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`}),);
批次處理範例回應
伺服器範例輸出
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。
成功回應
JSON 回應範例
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}}
錯誤回應
JSON 回應範例
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 狀態
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 錯誤碼
可用的碼和 JSON-RPC 碼
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,NOT_IMPLEMENTED: -32603,// Implementation specific errorsUNAUTHORIZED: -32001, // 401FORBIDDEN: -32003, // 403NOT_FOUND: -32004, // 404METHOD_NOT_SUPPORTED: -32005, // 405TIMEOUT: -32008, // 408CONFLICT: -32009, // 409PRECONDITION_FAILED: -32012, // 412PAYLOAD_TOO_LARGE: -32013, // 413UNPROCESSABLE_CONTENT: -32022, // 422TOO_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,NOT_IMPLEMENTED: -32603,// Implementation specific errorsUNAUTHORIZED: -32001, // 401FORBIDDEN: -32003, // 403NOT_FOUND: -32004, // 404METHOD_NOT_SUPPORTED: -32005, // 405TIMEOUT: -32008, // 408CONFLICT: -32009, // 409PRECONDITION_FAILED: -32012, // 412PAYLOAD_TOO_LARGE: -32013, // 413UNPROCESSABLE_CONTENT: -32022, // 422TOO_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 定義,以閱讀更多詳細資訊