Skip to content

useInfiniteQuery should support dynamic pagination parameter location #2352

Open
@DevSnap

Description

@DevSnap

Description

The current implementation of useInfiniteQuery in the generated client forces pagination via a pageParamName, injecting pageParam directly into the query parameters.

Example of the generated usage:

const { data } = api.useInfiniteQuery(
  "post",
  "/foods",
  {
    body,
  },
  {
    pageParamName: "pageIndex",
    initialPageParam: 1,
    getNextPageParam: (lastPage) => (lastPage.pageIndex) + 1,
  }
);

Internally, this injects the page parameter like so:

    useInfiniteQuery: (method, path, init, options, queryClient) => {
      const { pageParamName = "cursor", ...restOptions } = options;
      const { queryKey } = queryOptions(method, path, init);
      return useInfiniteQuery(
        {
          queryKey,
          queryFn: async ({ queryKey: [method, path, init], pageParam = 0, signal }) => {
            const mth = method.toUpperCase() as Uppercase<typeof method>;
            const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
            const mergedInit = {
              ...init,
              signal,
              params: {
                ...(init?.params || {}),
                query: {
                  ...(init?.params as { query?: DefaultParamsOption })?.query,
                  [pageParamName]: pageParam,
                },
              },
            };

            const { data, error } = await fn(path, mergedInit as any);
            if (error) {
              throw error;
            }
            return data;
          },
          ...restOptions,
        },
        queryClient,
      );
    },

This implementation assumes that pagination is always query-based and can be controlled by injecting pageParam via a named key (e.g., pageIndex). However, this does not work for APIs where pagination is passed in the request body or headers.

Proposal

Introduce a new optional parameter applyPagination:

const { data } = api.useInfiniteQuery(
  "post",
  "/foods",
  {
    body,
  },
  {
    applyPagination: ({ pageParam }) => ({
      body: { page: pageParam },
      //or maybe
      headers: { "X-Page": pageParam }
    }),
    initialPageParam: 1,
    getNextPageParam: (lastPage) => (lastPage.pageIndex) + 1,
  }
);

Internally, this function is called to merge pagination data into the request options:

useInfiniteQuery: (method, path, init, options, queryClient) => {
 // ...code omitted for brevity
  const mergedInit = {
    ...init,
    signal,
    ...applyPagination?.({ pageParam }),
  };
// ...more code follows

Extra

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions