TanStackT
TanStack2y ago
3 replies
standard-azure

How to get all of a paginated resource

Currently I've built this FN that serially fetches all of a resource:

import type { QueryFunction } from '@tanstack/react-query';
import type { Metadata, ResourceListOptions } from '@dn-types/api';

type PaginatedResourceQueryFn<T> = (options: ResourceListOptions) => Promise<{ data: T[]; metadata?: Metadata }>;

/**
 * Creates a query function for react-query which will:
 *
 * - fetch a 500-item page from a list endpoint,
 * - check whether there are more rows,
 * - fetch the next page and check again
 * - repeat
 *
 * It does this until it has fetched all results or has made maxPages (default 10) pages of requests.
 */
export function makeMaxResourceQueryFn<T>(
  queryFn: PaginatedResourceQueryFn<T>,
  maxPages = 10
): QueryFunction<{ data: T[]; metadata?: Metadata }> {
  return async ({ signal }) => {
    let allResources: T[] = [];

    let currentResponse;
    let currentPage = 1;
    do {
      // Fetch resource at least once, append to `allResources`, then continue if we have more pages.
      currentResponse = await queryFn({
        cursor: currentResponse?.metadata?.nextCursor,
        pageSize: 500,
        init: { signal },
      });
      allResources = allResources.concat(currentResponse.data);
      currentPage++;
    } while (currentResponse.metadata?.hasNextPage && currentPage <= maxPages);

    return {
      data: allResources,
      metadata: {
        hasNextPage: false,
        hasPrevPage: false,
        totalCount: allResources.length,
        nextCursor: '',
        prevCursor: '',
      } satisfies Metadata,
    };
  };
}


Obvious issue is that we fetch serially; problem is the cursors are opaque, so we can't just do math to figure out the next cursor. In that case, this may be optimal.
Was this page helpful?