非 JSON 内容类型
除了 JSON 序列化数据外,tRPC 还可以使用 FormData、File 和其他二进制类型作为过程输入。
¥In addition to JSON-serializable data, tRPC can use FormData, File, and other Binary types as procedure inputs
客户端设置
¥Client Setup
虽然 tRPC 原生支持多种非 JSON 序列化类型,但你的客户端可能需要根据你的设置进行一些链接配置才能支持这些类型。
¥While tRPC natively supports several non-json serializable types, your client may need a little link configuration to support them depending on your setup.
httpLink
开箱即用地支持非 JSON 内容类型,如果你只使用此功能,则你现有的设置应该可以立即生效。
¥httpLink
supports non-json content types out the box, if you're only using this then your existing setup should work immediately
ts
import { httpLink } from '@trpc/client';trpc.createClient({links: [httpLink({url: 'http://localhost:2022',}),],});
ts
import { httpLink } from '@trpc/client';trpc.createClient({links: [httpLink({url: 'http://localhost:2022',}),],});
但是,并非所有链接都支持这些新的内容类型。如果你使用 httpBatchLink
或 httpBatchStreamLink
,则需要包含 splitLink 并根据内容检查要使用的链接。
¥However, not all links support these new content types, if you're using httpBatchLink
or httpBatchStreamLink
you will need to include a splitLink and check which link to use depending on the content
ts
import {httpBatchLink,httpLink,isNonJsonSerializable,splitLink,} from '@trpc/client';trpc.createClient({links: [splitLink({condition: (op) => isNonJsonSerializable(op.input),true: httpLink({url,}),false: httpBatchLink({url,}),}),],});
ts
import {httpBatchLink,httpLink,isNonJsonSerializable,splitLink,} from '@trpc/client';trpc.createClient({links: [splitLink({condition: (op) => isNonJsonSerializable(op.input),true: httpLink({url,}),false: httpBatchLink({url,}),}),],});
如果你在 tRPC 服务器中使用 transformer
,TypeScript 要求你的 tRPC 客户端链接也定义 transformer
。以此示例为基础:
¥If you are using transformer
in your tRPC server, typescript requires that your tRPC client link defines transformer
as well.\ Use this example as base:
ts
import {httpBatchLink,httpLink,isNonJsonSerializable,splitLink,} from '@trpc/client';import superjson from 'superjson';trpc.createClient({links: [splitLink({condition: (op) => isNonJsonSerializable(op.input),true: httpLink({url,transformer: {// request - convert data before sending to the tRPC serverserialize: (data) => data,// response - convert the tRPC response before using it in clientdeserialize: superjson.deserialize, // or your other transformer},}),false: httpBatchLink({url,transformers: superjson, // or your other transformer}),}),],});
ts
import {httpBatchLink,httpLink,isNonJsonSerializable,splitLink,} from '@trpc/client';import superjson from 'superjson';trpc.createClient({links: [splitLink({condition: (op) => isNonJsonSerializable(op.input),true: httpLink({url,transformer: {// request - convert data before sending to the tRPC serverserialize: (data) => data,// response - convert the tRPC response before using it in clientdeserialize: superjson.deserialize, // or your other transformer},}),false: httpBatchLink({url,transformers: superjson, // or your other transformer}),}),],});
服务器使用
¥Server Usage
当 tRPC 处理请求时,它会根据请求的 Content-Type
标头解析请求正文。如果你遇到类似 Failed to parse body as XXX
的错误,请确保你的服务器(例如 Express、Next.js)在 tRPC 处理请求正文之前没有解析它。
¥When a request is handled by tRPC, it takes care of parsing the request body based on the Content-Type
header of the request.\ If you encounter errors like Failed to parse body as XXX
, make sure that your server (e.g., Express, Next.js) isn't parsing the request body before tRPC handles it.
ts
// Example in express// incorrectconst app = express();app.use(express.json()); // this try to parse body before tRPC.app.post('/express/hello', (req,res) => {/* ... */ }); // normal express route handlerapp.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC fails to parse body// correctconst app = express();app.use('/express', express.json()); // do it only in "/express/*" pathapp.post('/express/hello', (req,res) => {/* ... */ });app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC can parse body
ts
// Example in express// incorrectconst app = express();app.use(express.json()); // this try to parse body before tRPC.app.post('/express/hello', (req,res) => {/* ... */ }); // normal express route handlerapp.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC fails to parse body// correctconst app = express();app.use('/express', express.json()); // do it only in "/express/*" pathapp.post('/express/hello', (req,res) => {/* ... */ });app.use('/trpc', trpcExpress.createExpressMiddleware({ /* ... */}))// tRPC can parse body
FormData
输入
¥FormData
Input
FormData 是原生支持的,对于更高级的用法,你还可以将其与 zod-form-data 之类的库结合使用,以类型安全的方式验证输入。
¥FormData is natively supported, and for more advanced usage you could also combine this with a library like zod-form-data to validate inputs in a type-safe way.
ts
import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .instanceof (FormData )).mutation ((opts ) => {constdata =opts .input ;return {greeting : `Hello ${data .get ('name')}`,};}),});
ts
import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .instanceof (FormData )).mutation ((opts ) => {constdata =opts .input ;return {greeting : `Hello ${data .get ('name')}`,};}),});
更高级的代码示例,你可以查看我们的 示例项目在此
¥For a more advanced code sample you can see our example project here
File
和其他二进制类型输入
¥File
and other Binary Type Inputs
tRPC 将许多八位字节内容类型转换为 ReadableStream
,可在过程中使用。目前支持的接口有 Blob
、Uint8Array
和 File
。
¥tRPC converts many octet content types to a ReadableStream
which can be consumed in a procedure. Currently these are Blob
Uint8Array
and File
.
ts
import {octetInputParser } from '@trpc/server/http';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({upload :publicProcedure .input (octetInputParser ).mutation ((opts ) => {constdata =opts .input ;return {valid : true,};}),});
ts
import {octetInputParser } from '@trpc/server/http';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({upload :publicProcedure .input (octetInputParser ).mutation ((opts ) => {constdata =opts .input ;return {valid : true,};}),});