Wrapping useQuery into a custom hook

I'm trying to wrap useQuery into a custom hook (as I have some legacy code that I need to run before). Example:
trpc.myendpoint.useQuery({ foo: 'bar' });
// instead of the code above, I want to write this:
useCustomHook(trpc.myendpoint, { foo: 'bar' })
trpc.myendpoint.useQuery({ foo: 'bar' });
// instead of the code above, I want to write this:
useCustomHook(trpc.myendpoint, { foo: 'bar' })
I'm able to write the JavaScript code, but I have trouble getting the TypeScript types right. I have more information in the Stackoverflow question here: https://stackoverflow.com/q/77096846 Thanks for any help ❤️
30 Replies
funki
funki9mo ago
@thomasplayschess TrpcProcedure is definitely not right because it could be a query or a mutation or an observable/subscription. if you wanna "hard-code" useQuery into your function without testing for the other possible types, use a narrower type instead. idk which type that would be but probably something like TrpcQueryProcedure. (might be named differently, just guessing). you'll find the right one for sure.
thomasplayschess
I found DecorateProcedure which works like this: DecorateProcedure<AppRouter['_def']['record']['PROCNAME'], any, 'PROCNAME'> but was not able to make this work for all procedures. Using a narrower type works, but I want this to be generic. I get it to work for a single procedure, but I thought there must be a type that makes this work across all procedures.
funki
funki9mo ago
well first of all you don't want to make it work for all procedures obviously, only for the query procedures. otherwise you wouldn't ask for useQuery specifically. right?
thomasplayschess
Oh, okay. My bad. You are right. Yes, sorry, wasn't sure on the wording.
funki
funki9mo ago
that type doesn't sound like what you're looking for. can you send the type definition of TrpcProcedure? it might be a union of the stuff you need.
thomasplayschess
TrpcProcedure is the one that I made up in the stackoverflow question.
funki
funki9mo ago
oh
thomasplayschess
For example this one: type TrpcProcedure = (typeof trpc)[keyof AppRouter['_def']['procedures']]; (which doesn't work)
funki
funki9mo ago
you wanna dive into tRPC's types. ctrl-click an existing, working useQuery it might be scary tho
thomasplayschess
That's what I did to get to DecorateProcedure<AppRouter... but these types are really like A extends B extends C extends D | E extends F Seems it would take me days to get into it tbh 🙈
funki
funki9mo ago
i don't think you need to derive the generic type from your own router. it's possible to write a function like what you want that should work with any router.
thomasplayschess
As you said, it's super scary...
funki
funki9mo ago
have you tried AnyQueryProcedure? i just looked around the tRPC docs and searched for "types" and eventually came across that.
thomasplayschess
I think I used this somewhere. But honestly I think I've used up to 10 different interfaces up to now. Problem is that many are also generic and look just like the DecorateProcedure above (with 3 generics).
funki
funki9mo ago
you'll certainly need another type that the queryprocedure type takes as an argument in order to contract the params to it. i'm looking for a tRPC react playground right now
thomasplayschess
Yeah, I had big problems regarding figuring out which interface/generic has which job... I'll give that AnyQueryProcedure another try that you've mentioned.
funki
funki9mo ago
no, that one ain't it because it's literally typed with any, but it's definition is a lead
thomasplayschess
Yes 😦 This is the type I'm getting when looking at trpc.oneofmyroutes:
object & {
getQueryKey: (input: {
// MY INPUT PARAMS
}, type?: QueryType | undefined) => QueryKey;
useQuery: ProcedureUseQuery<...>;
useSuspenseQuery: <TQueryFnData extends {
...;
} = {
...;
}, TData = TQueryFnData>(input: {
// ALSO MY INPUT PARAMS (?)
}, opts?: Omit<...> | undefined) => [...];
}
object & {
getQueryKey: (input: {
// MY INPUT PARAMS
}, type?: QueryType | undefined) => QueryKey;
useQuery: ProcedureUseQuery<...>;
useSuspenseQuery: <TQueryFnData extends {
...;
} = {
...;
}, TData = TQueryFnData>(input: {
// ALSO MY INPUT PARAMS (?)
}, opts?: Omit<...> | undefined) => [...];
}
Thanks for taking the time btw! 🙂 Much appretiated
funki
funki9mo ago
okay seems like my instinct was wrong and you're already on the right track with DecorateProcedure
thomasplayschess
At least I got that right... But no clue how I make DecorateProcedure<TProcedureOrRouter, TFlags, TPath> generic now 🙈
funki
funki9mo ago
@thomasplayschess i think i got it
const runProcedure = <
QueryProcedure extends AnyQueryProcedure,
U,
V extends string
>
(
proc: DecorateProcedure<QueryProcedure, U, V>,
params: inferProcedureInput<QueryProcedure>
) => {
return proc.useQuery(params)
}

runProcedure(trpc.foo, "aoidwoij")
const runProcedure = <
QueryProcedure extends AnyQueryProcedure,
U,
V extends string
>
(
proc: DecorateProcedure<QueryProcedure, U, V>,
params: inferProcedureInput<QueryProcedure>
) => {
return proc.useQuery(params)
}

runProcedure(trpc.foo, "aoidwoij")
about U and V: dont know + dont care
thomasplayschess
AWESOME, I'll try it 🥳 YESSS
funki
funki9mo ago
@thomasplayschess maybe you can take a look at my issue, it's from 1-2 days ago and starts with "404 TRPCError"
thomasplayschess
Sure! Thank you very much again. And if you want stackoverflow points please answer the question and I will accept it.
thomasplayschess
WTF, I've used the "mark as solved" on the wrong message (thought it would apply to the whole thread) and now it believes that the emoji is the answer 🤦‍♂️ Well... Looks like I cannot undo my mistake. (just FYI, if anyone comes across this)
funki
funki9mo ago
nah i'm not on that SO grind
thomasplayschess
Actually, it's really close... But not 100% working as I've only just now noticed. The type hints, etc. is all working great for the input, but the output is always any.
const result = runProcedure(trpc.foo, "aoidwoij");
result.data // type is always any
const result = runProcedure(trpc.foo, "aoidwoij");
result.data // type is always any
Maybe AnyQueryProcedure is not the right type. I will give this another try tomorrow... 🙈
funki
funki9mo ago
oh huh i think it can be fixed will check when i'm home
thomasplayschess
I got it btw, had to add UseTRPCQueryResult<inferProcedureOutput<QueryProcedure>, TRPCClientErrorLike<QueryProcedure>> as return type. But thanks again for your help. I wouldn't have been able to solve this without your help 🙂
funki
funki9mo ago
nice!
More Posts
Pass additional data from `useQuery` to contextFor `splitLink` there is a way to pass context value through `useQuery` as second param `{ trpc: { cCannot set headers in procedures with fetch adapterAm i right that with such a setup in nextjs app router route handler: ```ts const handler = async (rDoes anybody now how to fix cors policy error?I am facing this error: Access to fetch at 'http://localhost:3002/trpc/getBotTag?batch=1&input=%7B%7how to handle error from Zod in trpc?I would like to return error instead throwing it away so i could show user nice feedback i found htt404 TRPCError: no query procedure on pathHi, this is my entire standalone tRPC server:```ts import { initTRPC } from "@trpc/server"; import {Call multiple TRPC endpoints from onSuccess()?I would like to create an API chain for analysing a geographic area where one successful API call caHow to set sizeLimit > 1MB (to solve 413 error)?Hi all, I am trying out the T3 stack where I want to send area polygons given by the user to my NexHow to manage server to server communicationWould managing headers this way work in nextjs as server to server communication? or does next managReplacing the dot notation in URLs with forward slash `/` ?is there a way to replace the dot separation with a forward slash instead ? so instead of `/api/tWhich one method should I use for creating trpc for server componetns in Nextjs 13 App dirHi which one I should use experimental_createTRPCNextAppDirServer or createTRPCProxyClient when it c