Any experiences with Query?

Has anyone tried out Query?

Especially with Meteor? At first look it looks interesting and I will dive more into it soon (hopefully) to see if this is something that we could use with Meteor+React.

1 Like

We are looking to move from React Router to Tanstack Router after migrating to meteor 3.0. The main reason is that we don’t want to follow the react-router data fetching pattern. We are also looking forward to using Tanstack Query so +1 for us here.

Just note that this pattern of fetch-on-render goes against the direction where React is pushing the community

I did, will not start a new project without it, it is super easy to use

import { useQuery } from '@tanstack/react-query';
import { Meteor } from 'meteor/meteor';
import { useSnackbar } from 'notistack';

import log from '../logger';

export function useMethod(methodName, params) {
  const data = Meteor.call(methodName, params);
  return { isLoading: false, data };
}

function useQueryMethod(methodName, methodData, initialValue, options = {}) {
  const snackbar = useSnackbar();
  if (Meteor.isServer) {
    return useMethod(methodName, methodData);
  }
  const { enabled = true } = options;
  const queryFn = ({ queryKey }) => {
    return new Promise((resolve, reject) => {
      Meteor.apply(methodName, [queryKey[1]], {
        wait: false,
        onResultReceived: (err, res) => {
          if (err) {
            reject(err);
          }
          resolve(res || null);
        },
      });
    });
  };
  const queryKey = [methodName];
  if (methodData) {
    queryKey.push(methodData);
  }
  const { isLoading, isFetching, ...others } = useQuery({
    queryKey,
    queryFn,
    retry: 1,
    onError: error => {
      log.error(`Erreur ${methodName}`, error);
      snackbar?.enqueueSnackbar('Erreur');
      options?.onError?.(error);
    },
    refetchOnWindowFocus: false,
    refetchOnMount: false,
    refetchOnReconnect: false,
    placeholderData: initialValue,
    enabled,
    staleTime: Infinity,
    ...options,
  });
  return {
    isFetching,
    isLoading: enabled ? isLoading || isFetching : isLoading,
    ...others,
  };
}

export default useQueryMethod;

and then

const { data: templates } = useQueryMethod(
    'Pages.getTemplates', //method names
    { schoolId }, // params
    [], // initialValue
  );

I’m also using it in a project I’m working on with Meteor 3.0. It works really well. The way I use it is pretty similar to what @sabativi is doing:

import { Meteor } from "meteor/meteor";
import { useQuery } from "@tanstack/react-query";

export const useQueryData = (queryName, params) => {
  const { isPending, error, data, refetch } = useQuery({
    queryKey: [queryName],
    queryFn: () => Meteor.callAsync(queryName, params),
  });

  return {
    isPending,
    data,
    error,
    refetch,
  };
};

And then:

const {
    data: comments,
    isPending: isLoadingComments,
    refetch,
  } = useQueryData(COMMENTS_QUERIES_NAME.getComments, { postId });

We use it on Galaxy 2 with the grubba-rpc package. It works really well, and the DX is great.

I’m a big fan of the “TanStack”.

Interesting! I’m thinking that if that is the case we could create an official wrapper or extension. But what I’m seeing here is method calls, I would love to integrate this with subscriptions and minimongo. Kind of goes to the ground:db part as well and would love to have it together with jam:pub-sub as well.

2 Likes

Yeah. I want to develop it further to support real-time.

Not sure how we could use minimongo here because Query already has a cache :thinking:

I can also recommend SWR. We use it as follows:

const { data, isLoading } = useMethod<ProductType[]>(
  'getCompanyProducts',
  [companyId],
);

And here’s the code:

type Args = readonly (EJSONable | EJSONableProperty)[];
type MethodWithArgs = [string, ...Args];

function fetcher<T>(methodWithArgs: MethodWithArgs) {
  return Meteor.callPromise(...methodWithArgs) as Promise<T>;
}

export function useMethod<T>(
  method: string,
  args: Args | null,
  config?: SWRConfiguration<T, Meteor.Error, Fetcher<T, MethodWithArgs>>,
) {
  return useSWR<T, Meteor.Error, null | MethodWithArgs>(
    args && [method, ...args],
    fetcher,
    {
      revalidateIfStale: false,
      revalidateOnFocus: false,
      revalidateOnReconnect: false,
      ...config,
    },
  );
}

It supports caching, deduplication, automatic refreshing, lazy execution, etc.

1 Like

I think the main thing that this touches on is this discussion, that is how I came to create this topic:

So I think we should get a list of things that we want as a start. Since we have minimongo the big thing is if we want to keep that or upgrade it into something more advance (like ground:db) is partially doing.

Looking at Tanstack Query, the offline mode is a simple saving of existing data and queries to localstorage (and seems to be currently experimental).

The ground:db approach seems to be far more advanced. Thinking about existing features, the simpler route will be:

  • support offline mode with minimongo e,g, offline minimongo
  • support a tanstack query extension for methods
  • align tanstack query offline mode with offline minimongo

On the other hand, I do not know much about minimongo to know how easy or how hard to “replace” it in exchange of getting offline mode.

I think offline mode for minimongo has been attempted before, ground:db is an iteration on that. I think adding some extension to Query could work, but the issues are the methods, we would need some sort of translator to move the received data into storage.

Maybe that should be the first steps:

  1. Create browser offline db compatible with minimongo
  2. Create extension to update the db from other sources than minimongo

Then whether it is Tanstack query, swr, or whatever utility, the community can create their respective extensions to bridge the data queried

I’ve successfully implemented Tanstack query into one of my projects, and I’m happy with how it worked out. Before the implementation it relied heavily on publications, but for improved performance I wanted to switch some of these to methods and I used Tanstack query here to manage all the caching/refetching/loading states/error states that you would normally have to do all by yourself when loading data with methods.

This project also required an offline functionality where users could toggle a project for offline availability and any of the changes made to it would be done only on device until a sync button was clicked. Using query this was also doable and even allowed for offline mutations using meteor methods. Mutations | TanStack Query React Docs

I’m also a big fan of the Zoderns relay typesafe methods package, that will help with type safety from server to client and this works really well with tanstack query as well.

I would be happy to create a small starter project with a todo example and the great query devtools for people to try it out themselves. I will see if I have some time this weekend to build something.

If there are any requests of specific parts to focus on or features to showcase let me know!

1 Like

I’ve posted a first version of the Tanstack Query demo here: GitHub - timsun28/tanstack-query-todo

Feel free to play around and let me know if you have any questions or if you run into any issues.

I’ve tested this version with the offline toggle in chrome and it works properly where even after a refresh it will send the data afterwards.

I also used this as a first test for myself using meteor 3.0, and I was able to get everything working, expect the Zodern relay package for typesafe methods.

I kept getting errors like this: Exception while invoking method ‘todos.finish’ TypeError: Promise.await is not a function where I think this has something to do how the methods are called through the package, but I wasn’t able to get this fixed.

In the queries.ts file on line 11 you can comment out the function from the relay method and start debugging from there, I have already added the package as a local package to get it to load by adding the typescript rc version to the package.js file.

3 Likes