T
tRPC

❓-help

Extending the client with "virtual" procedures

Aanglinb1/11/2024
I'm hoping to write a virtual mutation which runs on the client and calls some other client-side apis and a different server procedure. I want to distribute my tRPC client with an "upload" procedure which takes in a File as a argument but instead of actually sending that over the wire, I want to call to another RPC to get a singed s3 url, then use that url to upload the file, then finally return a url pointing to the uploaded file. Here's a fake example of what that could look like:
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000'
}),
],
virtual: {
upload: virtualProdedure
.input({ file: z.any() })
.mutation((input) => {
// Call whatever you want here
return {
'url': 'https://example.com'
}
})
}
})

trpc.upload.mutate({ file: new File() })
const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000'
}),
],
virtual: {
upload: virtualProdedure
.input({ file: z.any() })
.mutation((input) => {
// Call whatever you want here
return {
'url': 'https://example.com'
}
})
}
})

trpc.upload.mutate({ file: new File() })
I have created my own out of band class for doing what I've described that hides the complexity from the caller but requires the caller to manage another object other than just tRPC. This has the big drawback of not allowing the caller to use any tRPC client. I have to re-implement a react-query implementation and direct imperative implementation to have rough parity with existing tRPC clients. Please let me know if that makes sense!

Looking for more? Join the community!