Skip to main content

Server-side row model

The server-side model requests block-aligned row slices as the viewport or page needs them. Filtering, sorting, grouping, and full-data aggregation are sent to the data source instead of calculated over an incomplete browser cache.

Loading interactive example…
React
const dataSource: IServerSideDataSource = {
async getRows({ request }) {
const response = await fetch("/api/orders", {
method: "POST",
body: JSON.stringify(request),
});
return response.json(); // { rows, totalRows }
},
};

<Grid rowModelType="serverSide" serverSideDataSource={dataSource} />

Request contract

getRows({ request }) receives startRow, endRow, active filters, ordered sorts, groupBy, the current groupKeys path, and requested aggregates. Return { rows, totalRows }; root responses may also provide columns and a schemaVersion for server-driven schemas.

import type { IServerSideDataSource } from "@agility-workbench/grid";

const serverSideDataSource: IServerSideDataSource = {
async getRows({ request }) {
const response = await fetch("/api/orders", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(request),
});
return response.json(); // { rows, totalRows }
},
};

const options = {
rowModelType: "serverSide",
serverSideBlockSize: 100,
serverSideDataSource,
} satisfies GridOptions;

Server-provided schema

const serverSideDataSource: IServerSideDataSource = {
async getRows({ request }) {
return {
rows: await loadRows(request),
totalRows: 500,
schemaVersion: "orders-v2",
columns: [
{ key: "orderNo", label: "Order" },
{ key: "total", label: "Total", type: ColumnType.CURRENCY },
],
};
},
};

A server-driven schema applies only while the application has not supplied column definitions itself — through columnDefs at creation, a wrapper prop, updateGridOptions({ columnDefs }), or api.setColumnDefs. Application definitions always win; release them with updateGridOptions({ columnDefs: undefined }) to let the server's schema apply again. An unchanged schema is deduped by schemaVersion (or by structural signature when no version is sent), so resending it with every block is cheap.

Omitting totalRows creates an open-ended listing. The grid probes forward until a short block establishes the end and displays provisional pagination totals with a + suffix.

Refresh server data

await api.refreshServerSideData();
await api.refreshServerSideData({ purge: true });
await api.refreshServerSideData({ groupKeys: ["EMEA"], purge: false });

Use a soft refresh to keep visible rows while refetching, or { purge: true } to discard affected blocks immediately. A groupKeys path limits refresh to one subtree.

For server-side pagination with expensive counts, pair an open-ended listing with a hint:

const options = {
rowModelType: "serverSide",
pagination: true,
pageSize: 50,
paginationUnknownTotalTooltip: "The server is still discovering the total",
serverSideDataSource,
} satisfies GridOptions;