Skip to content

@RestListing — external REST listing rows

@RestListing fills a listing’s rows from an arbitrary (non-Mateu) REST endpoint, fetched client-side: the renderer calls the URL directly — no Mateu server mediating — navigates to the array in the JSON response, and maps each item into a row by reading each column by its field name (so a Row(String code, String name) reads code/name from each item). The columns come from the Listing<Row>’s Row type as usual.

It is the listing sibling of @RestOptions — the same decoupling idea, one level up: a Mateu table talking to any REST API.

Target: TYPE (and ANNOTATION_TYPE, so it composes as a semantic annotation).

@UI("/rest-countries")
@RestListing(url = "/countries.json", itemsPath = "data.countries")
public class RestCountries implements Listing<RestCountries.Row> {
public record Row(String code, String name, long population) {}
// Never called — the rows are fetched client-side — so it may return empty.
@Override public ListingData<Row> search(SearchRequest r, HttpRequest h) {
return ListingData.of();
}
}

Given a response like { "data": { "countries": [ { "code": "ES", "name": "Spain", "population": 47 }, … ] } }, the table shows a Code / Name / Population grid populated from the endpoint.

AttributeDefaultMeaning
urlThe endpoint URL. Supports ${state.x} interpolation — including ${searchText}, ${page}, ${size}.
methodGETThe HTTP method.
headers{}Request headers as "Name: Value" strings (values interpolated).
body""A request body template (interpolated) for non-GET methods.
itemsPath""A dot path to the array inside the response; blank means the response root is the array.

@RestListing travels on the wire as Crudl.rowsSource (a RestDataSource descriptor). When the listing renderer sees a rowsSource, it skips the server search action and instead fetches the endpoint client-side on mount and on every search, mapping each JSON item into a row object keyed by column id.

  • Search — the free-text query filters the fetched rows in memory (case-insensitive, any column). The interpolated url also carries ${searchText}, so an endpoint that supports server-side search gets it too — return the already-filtered set and it just works.
  • Pagination — applied in memory over the fetched rows (${page}/${size} are likewise interpolated into the url for endpoints that page server-side).

The endpoint must be reachable from the browser (CORS-friendly for cross-origin APIs; same-origin needs nothing). The listing is read-only.

Mateu.NET and the Python backend emit the same rowsSource descriptor:

.NET
[UI("rest-countries"),
RestListing("/countries.json", ItemsPath = "data.countries")]
public class RestCountries : Listing<RestCountries.Filters, RestCountries.Row>
{
public class Filters { }
public class Row { public string? Code { get; set; } public string? Name { get; set; } }
public override ListingData<Row> Search(SearchRequest r) => ListingData.From<Row>([]);
}
# Python
@ui("rest-countries")
@rest_listing(url="/countries.json", items_path="data.countries")
class RestCountries(Listing[Row]):
def search(self, request, http=None):
return []