connect-query-es icon indicating copy to clipboard operation
connect-query-es copied to clipboard

Add ability to easily update query cache

Open nickzelei opened this issue 1 year ago • 5 comments

Hey all, awesome project! This is allowing me to easily switch away from swr over to using tanstack/query.

One thing I miss however is the ability to easily update the query cache.

With swr, each instance has a mutate function that includes the curried key, so I can easily pass it the new value. With the current iteration of this library, the refetch function that is returned from useQuery takes no parameters.

In order for me to update the underlying cache, I have to do something like this:

const queryclient = useQueryClient();
const { mutateAsync } = useMutation(createMyValue);

async function onSubmit(values: MyFormValues): Promise<void> {
  const resp = await mutateAsync(...values...);
  queryclient.setQueryData(
    createConnectQueryKey(geyMyValue, {
      id: resp.myvalue.id,
    }),
    new GetMyValueResponse({
      myvalue: resp.myvalue,
    })
  );
}

I'd love it if there were an easier way to trigger a cache update for a useQuery value that didn't involve so much boilerplate.

nickzelei avatar Jul 12 '24 19:07 nickzelei

Thanks for the issue! In general, invalidating queries (triggering a refresh) instead of manipulating the cache is preferable because it's so simple and reliable (for example, you'll never forget to also update a lastUpdated property).

For example, I've often seen a pattern like this:

  const res = useMutation(createMyValue, {
    onSuccess: (data) =>
      queryClient.invalidateQueries({
        queryKey: createConnectQueryKey(geyMyValue, {
          id: data.id,
        }),
      })
  });

I'm not closely familiar with swr. Is it more common to update the cache than to let it update?

timostamm avatar Jul 15 '24 12:07 timostamm

Thanks for the response! Unclear if it's more common or not to do that. swr works effectively the same way as the refetch does with their mutate, but they optionally allow you to provide a cache value.

Since our mutation APIs like Create, Update return the latest object, I like to optimistically update the cache because we have the latest object instead of just calling refetch again to hit the API one more time.

Perhaps this is an anti-patten and it's just way more common to simply call refetch or invalidate.

nickzelei avatar Jul 15 '24 16:07 nickzelei

Since our mutation APIs like Create, Update return the latest object, I like to optimistically update the cache

I'm not sure I'd call it an anti-pattern, just that I would generally recommend to invalidate and refetch for simplicity. Makes perfect sense to me to not waste a roundtrip, if you know what you're doing 🙂

Here's the tanstack/query guide on this topic: https://tanstack.com/query/latest/docs/framework/react/guides/updates-from-mutation-responses

Maybe the most straight-forward improvement would be a function similar to this:

import {type Message, type PartialMessage,} from "@bufbuild/protobuf";
import {QueryClient} from "@tanstack/react-query";
import {createConnectQueryKey, type MethodUnaryDescriptor} from "@connectrpc/connect-query";

export function setQueryData<
  I extends Message<I>,
  O extends Message<O>,
>(
  client: QueryClient,
  methodDescriptor: Pick<MethodUnaryDescriptor<I, O>, "I" | "name" | "service">,
  input: PartialMessage<I>,
  output: O,
): void {
  const key = createConnectQueryKey<I, O>(methodDescriptor, input);
  client.setQueryData(key, output);
}

It just provides a bit of type-safety for RPCs on top of QueryClient.setQueryData. Maybe we can add it to the package if it works out well.

timostamm avatar Jul 16 '24 10:07 timostamm

Although I think we could make it a little easier with a custom QueryClient, we do have a helper method to handle these kinds of things: createProtobufSafeUpdater.

We've been internally playing with the idea of exporting a custom QueryClient that provides common helper methods for interacting with the query client in a typesafe way but that will probably wait till everything has been moved over to protobuf-es@v2 anyways.

paul-sachs avatar Jul 16 '24 15:07 paul-sachs

Thanks for the note @paul-sachs - I must have missed that function in the docs. Will have to go through my code and update the refs, that is very helpful! Apologies for the comment delay, my github emails need to be reworked... Saw that there is also more work happening in v2, which is exciting!

nickzelei avatar Oct 08 '24 20:10 nickzelei

Finally merged the changes needed here with 2.1.0 🚀 . We now have typesafe query client methods built into your bog standard query client 🥳 .

paul-sachs avatar May 22 '25 20:05 paul-sachs