Skip to content

Hydra data provider ignores hydra:view pagination links, hardcodes page/itemsPerPage #660

@dkarlovi

Description

@dkarlovi

Description

The Hydra data provider hardcodes page and itemsPerPage query parameters on every GET_LIST request, ignoring the cursor-based pagination links (hydra:next, hydra:previous) returned in hydra:view.

This makes it impossible to use cursor-based (keyset) pagination — a common pattern for APIs that avoid offset-based pagination for performance and consistency reasons.

Current behavior

In convertReactAdminRequestToHydraRequest (dataProvider.ts#L413-L414):

if (page) url.searchParams.set('page', page.toString());
if (perPage) url.searchParams.set('itemsPerPage', perPage.toString());

These params are always appended to the request URL, regardless of what the server's hydra:view links specify.

On the response side (dataProvider.ts#L602-L631), when hydra:totalItems is present, the provider returns { data, total } and never reads hydra:view. The hydra:view / pageInfo path is only reached when totalItems is absent — but even then, subsequent requests still send page=N instead of following the cursor URLs from hydra:next / hydra:previous.

Expected behavior

When a collection response includes hydra:view with hydra:next / hydra:previous links, the data provider should use those URLs for navigation instead of constructing page=N&itemsPerPage=X params. This is how Hydra's PartialCollectionView is designed to work — the server tells the client how to navigate.

Specifically:

  1. For the first page, the provider should request the collection URL (optionally with itemsPerPage / pageSize if the server supports it)
  2. For subsequent pages, the provider should follow hydra:next / hydra:previous URLs from the response's hydra:view
  3. When hydra:view is present without hydra:last, the provider should return pageInfo (not total) so react-admin uses next/prev navigation

Workaround

We currently bypass base.getList entirely in our data provider wrapper — we fetch the collection URL ourselves, track hydra:next/hydra:previous URLs from responses in a cache, and return pageInfo instead of total:

getList: async (resource, params) => {
    const page = params.pagination?.page ?? 1;
    const perPage = params.pagination?.perPage ?? 25;
    const cached = cursorCache.get(cursorKey(resource, params));

    let url;
    if (page > 1 && cached?.page < page && cached?.nextUrl) {
        url = new URL(cached.nextUrl, window.location.origin);
    } else if (page > 1 && cached?.page > page && cached?.previousUrl) {
        url = new URL(cached.previousUrl, window.location.origin);
    } else {
        url = new URL(`/api/${resource}`, window.location.origin);
        url.searchParams.set("pageSize", String(perPage));
    }

    const response = await fetch(url.toString(), {
        headers: { Accept: "application/ld+json" },
        credentials: "include",
    });
    const json = await response.json();
    const view = json["hydra:view"] ?? null;

    cursorCache.set(key, {
        nextUrl: view?.["hydra:next"] ?? null,
        previousUrl: view?.["hydra:previous"] ?? null,
        page,
    });

    return {
        data: json["hydra:member"].map(normalize),
        pageInfo: {
            hasNextPage: !!view?.["hydra:next"],
            hasPreviousPage: !!view?.["hydra:previous"],
        },
    };
}

This works but defeats the purpose of using the Hydra data provider in the first place.

Versions

  • @api-platform/admin: 4.2.4
  • react-admin: 5.6.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions