hagabaka
hagabaka16mo ago

Typed wrappers for procedures

I have a TRPC client with working queries and mutations. I wanted to create wrapper functions for all the procedure so that instead of trpc.someQuery.query(...) I can use someQuery(...), and instead of trpc.someMutation.mutate(...), someMutation(...). I'm having trouble with getting the wrapped functions to have correct parameter and return types. Currently I have:
function wrap<F extends ((...args: any[]) => any)>(fnWithClient: (c: typeof client) => F): (...args: Parameters<F>) => ReturnType<F> {
return (...args: Parameters<F>) => fnWithClient(getClient())(...args);
}
export const someQuery = wrap((client) => client.someQuery.query)
export const someMutation = wrap((client) => client.someMutation.mutation)
function wrap<F extends ((...args: any[]) => any)>(fnWithClient: (c: typeof client) => F): (...args: Parameters<F>) => ReturnType<F> {
return (...args: Parameters<F>) => fnWithClient(getClient())(...args);
}
export const someQuery = wrap((client) => client.someQuery.query)
export const someMutation = wrap((client) => client.someMutation.mutation)
It works in run time, and even shows correct types in VSCode, but when I generate a .d.ts, it says these functions have type (input?: unknown, options?: ProcedureOptions | undefined) => Promise<unknown>. How can I fix this issue, and is there a more elegant way to create such wrapper functions? I would prefer to use something like export const someQuery = wrap('someQuery').
9 Replies
trash
trash16mo ago
i wonder if this is what happens when you try to leverage types that are internal to the package, i have tried myself though tbh @julius does that sound right? just guessing
julius
julius16mo ago
Maybe you can get some inspiration from the withQuery() I did for cal.com https://github.com/calcom/cal.com/blob/446c29dd9c576103d7b3caa02317a65b43db0e9d/apps/web/lib/QueryCell.tsx#L86
GitHub
cal.com/QueryCell.tsx at 446c29dd9c576103d7b3caa02317a65b43db0e9d ·...
Scheduling infrastructure for absolutely everyone. - cal.com/QueryCell.tsx at 446c29dd9c576103d7b3caa02317a65b43db0e9d · calcom/cal.com
hagabaka
hagabaka16mo ago
Would AnyQueryProcedure include the type of trpc.someQuery?
hagabaka
hagabaka16mo ago
I don't understand DecorateQuery used in that function. Seems to be this gigantic type defined here https://github.com/trpc/trpc/blob/19c7c27d8ed548b2bc7ab5eb921fd179143aa230/packages/react-query/src/createTRPCReact.tsx#L87
GitHub
trpc/createTRPCReact.tsx at 19c7c27d8ed548b2bc7ab5eb921fd179143aa23...
🧙‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy. - trpc/createTRPCReact.tsx at 19c7c27d8ed548b2bc7ab5eb921fd179143aa230 · trpc/trpc
julius
julius16mo ago
DecorateProcedure is the type of like trpc.post.byId yes you're extending AnyProcedure - it will get narrowed on invocation
hagabaka
hagabaka16mo ago
When I try to assign const a: AnyQueryProcedure = client.someQuery, it says it is missing properties _def, _type, and _procedure I'm justing createTRPCProxyClient, not the with react
julius
julius16mo ago
so there's a similar type for the vanilla client - so that shouldn't be a problem why are you giving it the type implicitely? also - can you tell a bit more about the usecase? why do you want to do this?
const query = wrap(c => c.post.byId);
const data = query({ id: 1 });
const query = wrap(c => c.post.byId);
const data = query({ id: 1 });
hagabaka
hagabaka16mo ago
I have a mini library that used to have only functions that make fetch calls, and now I'm adding an TRPC API. I want to keep the exports consistent so prefer to export functions for the individual procedures instead of a TRPC client. Also I want the client to be lazily created when any of the procedures is used.
Nick
Nick16mo ago
Isn't this basically it?
function makeWrap<T>(t: T) {
return function wrap<Q>(func: (t: T) => Q) {
return func(t)
}
}

const wrap = makeWrap(trpcClient)
const someId = 1
wrap(c => c.post.byId.query(someId))
function makeWrap<T>(t: T) {
return function wrap<Q>(func: (t: T) => Q) {
return func(t)
}
}

const wrap = makeWrap(trpcClient)
const someId = 1
wrap(c => c.post.byId.query(someId))
Not exactly sure what's to be gained by using tRPC types in the implementation here, it would be useful to see a more complete example so we can advise
More Posts
Are TRPC server procedures actual endpoints? Meaning can you directly do a `post` request to them?Lets say you have a public procedure called `getHelloWorld`, can you hit it by doing `localhost:3000TRPCClientError - No "query"-procedure on path "user.all"Im using react native expo, prisma, trpc and the postgres database is on railway I have run the follAm I the only one struggling with pnpm + TypeScript monorepo + trpc?Hello all, When using pnpm in a TypeScript monorepo without `node-linker`, I hit those errors: ```tRPC standalone server in monorepoHi, I'm using t3-stack monorepo as my base and I've swapped out NextJS backend for standalone HTTP Codemod to v10 is not modifying any fileHello 👋, I must be super dumb but running `pnpm dlx trpc-v10-migrate-codemod ` in my project isn't No overload matches this call when outputting unionsHello there 👋, I have this simple procedure (we're not fully migrated on v10 yet, using interop): [help] Uncaught (in promise) TRPCClientError: Property description must be an object: uAfter building on linux, I visit the site. And Chrome console shows this error. But if I build on myHow can I make tRPC+NextJs APIs faster? (db and functions region is already same)Hi, I have migrated my website backend (https://clubofcoders.com) from NestJs + Prisma + Cloud Run tSuperjson "undefined"When returning a list from the db with a ton of potential undefined fields, superjson just crams a tThrowing fastify errors when using fastify adapterHello, I'm using `fastifyTRPCPlugin` from `@trpc/server/adapters/fastify` and trying to throw errors