LyteNyte Grid logo for light mode. Links back to the documentation home page.
Github repository for this project. 1771 Technologies home page
Server Data Loading

Data Interface

Use LyteNyte Grid's server data loading APIs to fetch and render row data from your backend.

LyteNyte Grid sends requests to the server using a defined request structure and expects responses in a specific format. This request/response cycle is the server data interface, i.e. the protocol LyteNyte Grid uses to exchange data.

Note

This section introduces the server data interface types and gives an overview. For a practical guide, see the Server Row Data guide.

Request Interface

The server data interface centers on the request interface. See the API reference for the full specification. The TypeScript definition of the DataRequest interface is shown below:

export interface DataRequest {
readonly id: string;
readonly path: (string | null)[];
readonly start: number;
readonly end: number;
readonly rowStartIndex: number;
readonly rowEndIndex: number;
}

Here is an example request:

{
id: "__root__:0-100",
path: [],
start: 0,
end: 100,
rowStartIndex: 0,
rowEndIndex: 100
}

The request id is "__root__:0-100”. LyteNyte Grid generates a unique id for each request but doesn’t guarantee the id format, so don’t use it in application logic. When two requests share the same id, they target the same rows, and their responses populate the same row range. This behavior is useful for deduplicating requests.

The path, start, and end properties determine the specific row slice a request targets. The server data source divides the view into row slices. Each slice is relative to the view root or a group row node. An empty path targets the root.

Info

The server data source represents rows as a tree. LyteNyte Grid flattens the tree and renders rows according to the current scroll position. In this documentation, the term “view” refers to the rows currently displayed.

The rowStartIndex and rowEndIndex project the placement of returned rows in the grid view, rather than acting as strict constraints. You can safely ignore these properties unless your server keeps the entire expanded and grouped table in memory.

When you use row groups, the requested slice can target a group expansion to fetch that group’s child nodes. For a grid grouped by "Group A" and "Group B", the data request is as follows:

// For a data slice that is an expansion of just Group A
{
id: "__root__:0-100",
path: ["Alpha"],
start: 0,
end: 100,
rowStartIndex: 0,
rowEndIndex: 100
}
// For a data slice that is an expansion of Group A followed by and expansion for a child node for Group B
{
id: "__root__:0-100",
path: ["Alpha", "Beta"],
start: 0,
end: 100,
rowStartIndex: 0,
rowEndIndex: 100
}

Consider the first request object, which has path: ["Alpha"]. The server should treat this as: “Fetch rows where Group A = “Alpha”.” LyteNyte Grid then groups the returned rows by Group B.

The second request object, with path: ["Alpha", "Beta"], targets leaf rows. In this case, Group A = “Alpha” and Group B = “Beta”.

This example shows how path defines the data slice the server should return. LyteNyte Grid combines these slices to build a coherent view.

Response Interface

The server receives data requests and returns responses. The response types are defined below. A single request can produce multiple responses, and LyteNyte Grid’s server data source supports this behavior.

There are two distinct response types:

  • Scrollable Rows: Detailed in Scrollable Rows API reference.
  • Pinned Rows (Top or Bottom): Detailed in the Pinned Rows API Reference.

LyteNyte Grid expects an array of responses. This allows the server to batch multiple response types into a single network payload.

Data Response for Scrollable Rows

The DataResponse interface defines the data response for scrollable rows. The TypeScript definition is as follows:

export interface DataResponse {
readonly kind: "center";
readonly data: (DataResponseLeafItem | DataResponseBranchItem)[];
readonly size: number;
readonly asOfTime: number;
readonly path: (string | null)[];
readonly start: number;
readonly end: number;
}

The response interface mirrors several request fields and adds additional fields. An example response looks like:

{
kind: "center",
data: [
{ kind: "leaf", id: "1", data: [1,2,3] },
// More rows as
],
size: 230_000,
asOfTime: 1759391811069, // Some Unix Timestamp
path: [],
start: 0,
end: 100
}

The scrollable row DataResponse object contains the following properties:

  • kind: Must be "center" to distinguish scrollable rows from pinned rows.
  • data: The row data returned by the server, containing leaf or branch data items.
  • size: Defines the relative row count for the response path. An empty path indicates the root row count. Row counts are relative to their path because grouping creates branches in the row tree. LyteNyte Grid flattens the tree to produce the rendered rows, and the final count reflects expanded and collapsed groups.
  • asOfTime: A Unix timestamp. It resolves collisions when multiple responses map to the same row. The response with the later asOfTime takes precedence. Conflicts can occur because requests are asynchronous and responses may arrive out of order.
  • path, start, end: Mirror the DataRequest properties. Avoid modifying these values in the response.

Leaf Row Data

Leaf row data uses the DataResponseLeafItem type. Its TypeScript definition is shown below:

export interface DataResponseLeafItem {
readonly kind: "leaf";
readonly id: string;
readonly data: any;
}

Like DataResponse, DataResponseLeafItem includes a kind property. The kind value must be "leaf". LyteNyte Grid uses this value to determine whether the response data creates a leaf row node.

Each DataResponseLeafItem creates one row node, and the item’s id maps directly to the row node’s id, associating the payload’s data with that specific row node.

Branch Row Data

Branch row data is represented by DataResponseBranchItem. LyteNyte Grid uses these items to create row group nodes. Row group nodes represent branches in the view that may be expanded or collapsed.

The TypeScript interface for DataResponseBranchItem is shown below.

export interface DataResponseBranchItem {
readonly kind: "branch";
readonly id: string;
readonly data: any;
readonly key: string | null;
readonly childCount: number;
}

The DataResponseBranchItem includes the following properties for group nodes:

  • kind: Must be "branch" to create a group node.
  • id: Maps to the group node’s id.
  • data: Set on the group node; it typically contains the group’s aggregated data.
  • key: Attached to the group node and used to position it within the server data source’s row tree.
  • childCount: Required. Indicates the number of child nodes in the group.

The close mapping between DataResponseBranchItem and the row group node is intentional. LyteNyte Grid maps the server response directly to node creation to improve performance.

Data Response For Pinned Rows

Pinned rows are always visible in the view, so LyteNyte Grid cannot detect changes to them. To address this, the server data source allows developers to push pinned row responses alongside scrollable row responses.

The DataResponsePinned interface defines the response type that LyteNyte Grid expects:

export interface DataResponsePinned {
readonly kind: "top" | "bottom";
readonly data: DataResponseLeafItem[];
readonly asOfTime: number;
}

The pinned response type is simpler than DataResponse because pinned rows are always leaf rows.

The kind property must be either "top" or "bottom", and tells LyteNyte Grid where to create the pinned row nodes. Like DataResponse, the data property contains the row data, and the asOfTime property resolves conflicts when writing rows to memory.

Query Fetcher Function

The LyteNyte Grid server data source requires a queryFn as the primary data-loading handler. The server data source calls queryFn whenever the view changes and passes the requests for the new view.

The function is responsible for fetching responses from the server, either with an HTTP request or a WebSocket message, and returning the data. A brief code example is shown below:

const ds = useServerDataSource<MyDataType>({
queryFn: (params) => {
return Server(params.requests);
},
queryKey: [], // Required
});

The queryFn function receives a params object, which includes the data requests for the current grid view. This object is described by the QueryFnParams type whose TypeScript definition is shown below:

export interface QueryFnParams<K extends unknown[]> {
readonly queryKey: K;
readonly requests: DataRequest[];
readonly reqTime: number;
readonly model: {
readonly rowGroupExpansions: Record<string, boolean | undefined>;
};
}

The queryFn returns a Promise of type (DataResponse | DataResponsePinned)[]. This array-based design lets LyteNyte Grid request data in slices, giving the queryFn implementation flexibility to optimize how it fetches data and returns responses.

The requests property contains the data requests for the current view. It is always an array, not a single item. Each request should have at least one matching response, but the server may return additional responses to preload data.

The reqTime property is a Unix timestamp of when the request was made. The server may use it to set the asOfTime, or ignore it if responses define their own freshness rules.

The model property describes the grid’s view configuration. This contains the current rowGroupExpansions state. You can use this value to determine if a group is expanded or collapsed when there are row groups applied to the grid.

Next Steps