Skip to content

useInfiniteQuery should support dynamic pagination parameter location #2352

@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

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions