JAVASCRIPT

Iterating Through Cursor-Based Paginated API Results

Efficiently fetch all data from APIs employing cursor-based pagination, ensuring you retrieve every record across multiple pages with async/await.

async function fetchAllPaginatedData(initialUrl, params = {}) {
    let allData = [];
    let nextUrl = initialUrl;
    let hasMore = true;

    while (hasMore) {
        try {
            const url = new URL(nextUrl);
            // Add initial parameters only for the first request if not already in nextUrl
            if (nextUrl === initialUrl) {
                Object.keys(params).forEach(key => url.searchParams.set(key, params[key]));
            }
            
            console.log(`Fetching from: ${url.toString()}`);
            const response = await fetch(url.toString(), {
                headers: {
                    'Accept': 'application/json',
                    // 'Authorization': 'Bearer YOUR_TOKEN' // Add if API requires authentication
                }
            });

            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }

            const result = await response.json();

            // Assuming data is in 'data' array and pagination info in 'meta' or top-level
            // Adapt this part based on your API's actual response structure
            if (result.data && Array.isArray(result.data)) {
                allData = allData.concat(result.data);
            }

            // Example: Cursor-based pagination common patterns:
            // - 'next_cursor' in meta or top-level
            // - 'links.next' providing a full URL
            // - 'pagination.next_page_url'

            const nextCursor = result.meta?.next_cursor || result.next_cursor;
            const nextPageUrl = result.links?.next || result.pagination?.next_page_url;

            if (nextCursor) {
                // Append cursor to original URL. Might need to handle existing params.
                // For simplicity, assuming initialUrl doesn't have a 'cursor' param initially.
                const newUrl = new URL(initialUrl);
                Object.keys(params).forEach(key => newUrl.searchParams.set(key, params[key]));
                newUrl.searchParams.set('cursor', nextCursor);
                nextUrl = newUrl.toString();
                hasMore = true;
            } else if (nextPageUrl) {
                // If API provides full next page URL directly
                nextUrl = nextPageUrl;
                hasMore = true;
            } else {
                hasMore = false;
            }

        } catch (error) {
            console.error('Error fetching paginated data:', error);
            hasMore = false; // Stop iteration on error
        }
    }
    return allData;
}

// Example Usage:
// const baseUrl = 'https://api.example.com/v1/items';
// fetchAllPaginatedData(baseUrl, { limit: 100 })
//     .then(items => {
//         console.log(`Fetched ${items.length} items.`);
//         // console.log(items);
//     })
//     .catch(err => console.error('Failed to fetch all items:', err));
How it works: This JavaScript snippet demonstrates how to consume a cursor-based paginated API, fetching all available data across multiple pages. Unlike offset-based pagination, cursor-based pagination uses an opaque string (cursor) to mark the last item fetched, making it more robust against data insertions/deletions during iteration. The `fetchAllPaginatedData` function continuously makes `fetch` requests, updating the URL with the `next_cursor` or `next_page_url` provided by the API response, until no further pages are indicated, accumulating all results into a single array.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs