Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use a custom react-query hook to attach an access token to every query

ive been working on this and stumping myself for actually 8 hours today. I figured it would be pretty easy to create a useAuthenticatedQuery hook to simply respond with the access token in the queryFn, but with typescript it has been a total nightmare. Any help would be appreciated

 const {accessToken} = useAuth()
 const subscriptionQuery = useQuery({
   queryKey: ["subscription", accessToken],
   queryFn: () => {
     return api.getSubscription(accessToken);
   },
});

This is the default query that im using out of the box in all of my screens. I dont want to add the access token to the key every time manually, and I dont want to call the useAuth hook for the access token every time.

this code is close to what i want to accomplish (roughly) but typescript has made it hell.

const useAuthenticatedQuery = (options: UseQueryOptions) => {
  const { accessToken } = useAuth();

  return useQuery({
    ...options,
    queryKey: [options.queryKey, accessToken],
    queryFn: (context) => {
      return options && options.queryFn && options.queryFn({ ...context, accessToken });
    },
  });
};

I understand that i could not attach the access token as context, and rather call context.queryKey and get it from that array, but I cannot add the access token as a query key without typescript throwing a fit. (err below)

No overload matches this call.
  The last overload gave the following error.
    Argument of type '{ queryKey: (string | QueryKey | undefined)[]; queryFn: (context: any) => unknown; context?: Context<QueryClient | undefined> | undefined; enabled?: boolean | undefined; ... 31 more ...; meta?: QueryMeta | undefined; }' is not assignable to parameter of type 'QueryKey'.
      Object literal may only specify known properties, and 'queryKey' does not exist in type 'readonly unknown[]'.ts(2769)

Any help is appreciated

This is the closest ive gotten

const useAuthenticatedQuery = <
  TQueryFnData = unknown,
  TError = unknown,
  TData = TQueryFnData,
  TQueryKey extends QueryKey = QueryKey
>(
  key: TQueryKey,
  fn: (accessToken: string) => QueryFunction<TQueryFnData, TQueryKey>,
  options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> = {}
) => {
  const { accessToken } = useAuth();

  return useQuery(key, fn(accessToken), {
    ...options,

    onError: (error) => {
      options.onError?.(error);
    },
  });
};

but that requires the function to look like this when called

const subscriptionQuery = useAuthenticatedQuery({
  ["subscription"],
  () => (accessToken) => api.getSubscription(accessToken),
  {},
})

Which is not only ugly, but also still doesnt solve the problem of the access token in the keys, and im posituive theres a better way

like image 714
user20715205 Avatar asked Oct 25 '25 19:10

user20715205


1 Answers

I think the answer is in this tweet: https://twitter.com/TkDodo/status/1491451513264574501

I've implemented it like this:

function useApi<
  TQueryKey extends [string, Record<string, unknown>?],
  TQueryFnData,
  TError,
  TData = TQueryFnData
>(
  queryKey: TQueryKey,
  fetcher: (params: TQueryKey[1], token: string) => Promise<TQueryFnData>,
  options?: Omit<
    UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
    "queryKey" | "queryFn"
  >
) {
  const { getToken } = useAuth();

  return useQuery({
    queryKey,
    queryFn: async () => {
      const token = await getToken();
      return fetcher(queryKey[1], token);
    },
    ...options
  });
}
like image 100
TkDodo Avatar answered Oct 28 '25 12:10

TkDodo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!