Recommended Normalization Approach
The recommended approach for normalizing inconsistent pagination metadata is to implement a dedicated Adapter Pattern within the Vue Storefront (VSF) integration layer. Instead of allowing backend-specific parameters (like offset, cursor, or search_after) to leak into the frontend components, the integration layer should act as a bidirectional translator.
1. The Translation Layer
Create a mapping utility that converts the VSF internal state (typically page and perPage) into the specific requirements of the headless provider. This ensures that if you switch providers or update the API version, you only modify the adapter, not the UI components.
- Request Mapping: Convert
page: 2, perPage: 20 $\rightarrow$ offset: 20, limit: 20 (for REST) or after: "cursor_id", first: 20 (for GraphQL).
- Response Mapping: Normalize the backend response into a standardized
PaginationMetadata object containing totalItems, totalPages, and hasNextPage.
2. Synchronizing State Without Total Counts
When a backend does not provide a total record count—common in high-performance cursor-based APIs or large Elasticsearch indices—you cannot calculate the total number of pages. To synchronize the frontend state, shift from Numbered Pagination to Infinite Scroll or "Load More" patterns.
Implement the following logic in the integration layer:
- Request an Extra Item: Request
limit + 1 items from the API.
- Determine Continuity: If the API returns
limit + 1 items, set hasNextPage: true and discard the extra item before passing the data to the store.
- UI State: Use the
hasNextPage boolean to toggle the visibility of the "Next" button or trigger the next fetch in an infinite scroll loop.
Practical Implementation & Verification
To verify the stability of your pagination mapping, especially for large datasets, use the following scoped tests:
# 1. Verify deep-paging limits (if using offset)
# Test if page 100 fails while page 1 succeeds
curl "https://api.provider.com/products?offset=2000&limit=20"
# 2. Check for record drift
# Compare the last item of page 1 with the first item of page 2
# to ensure no duplicates exist due to unstable sorting.
Assumptions and Constraints
- Sorting: This approach assumes a stable sort order is applied at the backend. Without a tie-breaker (e.g., sorting by
ID after Price), items may shift between pages during concurrent writes.
- Performance: Offset-based pagination is assumed to be slower for deep pages. If the dataset is massive, cursor-based mapping is strongly recommended over offset mapping.
Missing Diagnostic: Does your current UI requirement strictly mandate numbered page jumps (e.g., "Jump to Page 50"), or is a sequential "Next/Previous" flow acceptable? If the former is required, you must implement a separate count query or a cached approximation of the total records.