Row Data Source

Server Row Data Source

Modern applications often need to handle datasets too large to load entirely into a browser. To maintain optimal performance, LyteNyte Grid implements a strategy where only necessary data is loaded while keeping the majority on the server—commonly known as server-side or on-demand data loading.

LyteNyte Grid's server row data source enables partial data loading, transferring only what's needed to the browser at any given time.

As the name suggests, this data source requests information from an external source—typically your own server. The expected responses must adhere to interfaces defined by the LyteNyte Grid server data source specification.

Creating A Server Row Data Source

LyteNyte Grid provides a useServerDataSource hook to create a server data source. At minimum, you must supply a dataFetcher function responsible for requesting data from your server. Below is a basic server data source setup:

The Data Fetcher

The dataFetcher function is the workhorse of your data source implementation. It receives an array of AsyncDataRequestBlock objects representing data blocks the server should return. Each AsyncDataRequestBlock has the following interface:

export interface AsyncDataRequestBlock {
  readonly id: string;
  readonly blockKey: number;
  readonly path: string[];
  readonly rowStart: number;
  readonly rowEnd: number;
  readonly blockStart: number;
  readonly blockEnd: number;
}

Here's what each field means:

  • id: A unique identifier for this block, primarily used internally by LyteNyte Grid but also available for your custom block identification
  • blockKey: A number representing the block offset—if data is partitioned into blocks of 100 rows, a blockKey of 1 would correspond to rows 100-199 (block keys start at 0)
  • path: A path for this block, populated only when row groups are present. The path consists of parent keys, while an empty path indicates a root-level block
  • rowStart: The index of the first row in this block
  • rowEnd: The index of the last row in this block
  • blockStart: The first block index—for example, if the blockKey is 2 and block sizes are 100, then blockStart would be 200
  • blockEnd: The last block index for this block

The server data source may request multiple blocks depending on the current view configuration. Complex views with row grouping and pivots typically create more blocks, while simpler views usually request just one or two blocks.

Data Fetcher Response

Your dataFetcher function should return an AsyncDataResponse object based on the AsyncDataRequestBlock parameters:

export type AsyncDataResponse = {
  readonly rootCount?: number;
  readonly reqTime: number;
  readonly blocks: AsyncDataBlock[];
  readonly topBlock?: AsyncDataBlockPinned;
  readonly bottomBlock?: AsyncDataBlockPinned;
  readonly totalBlock?: AsyncDataBlockTotal;
};

The AsyncDataResponse may return more than what was requested, but at minimum it must return a response for each requested block in the blocks property.

Here's what each property represents:

  • rootCount: The number of root rows in the grid. Without grouping, this equals the total rowCount. With row groups, it represents the count of top-level rows without parents
  • reqTime: A Unix timestamp when the request was sent. Your server should return this value unchanged as it's used internally to ignore outdated requests
  • blocks: An array of responses for the requested blocks, containing AsyncDataBlock objects:
export type AsyncDataBlock = {
  readonly blockKey: number;
  readonly frame: {
    readonly data: unknown[];
    readonly ids: string[];
    readonly pathKeys: (string | null)[];
    readonly kinds: (RowGroupKind | RowLeafKind)[];
    readonly childCounts: number[];
  };
  readonly size: number;
  readonly path?: string[];
};

Each block contains:

  • blockKey: Same as the request blockKey
  • frame: Contains the block data as parallel arrays that must have equal length:
    • data: The row data
    • ids: Unique identifiers for each row
    • pathKeys: Path values for each row
    • kinds: Row type indicators (RowGroupKind is 2, RowLeafKind is 1)
    • childCounts: Number of child rows (0 for leaf rows)
  • size: The total size of this block
  • path: The block's path (empty or omitted for root blocks)

Additionally, your response can include pinned rows and totals using these interfaces:

export type AsyncDataBlockPinned = {
  readonly frame: {
    readonly data: unknown[];
    readonly ids: string[];
  };
};
 
export type AsyncDataBlockTotal = {
  readonly frame: {
    readonly data: unknown;
  };
};

For AsyncDataBlockPinned, the frame property should contain arrays of equal length, just like in blocks.

While this may seem complex initially, the examples will clarify the concepts. You'll find that the server data source interface is straightforward, with most complexity residing in your server's data processing layer.

Server Data Source
TODO

Row Grouping

Row grouping increases the number of blocks the server data source manages and diversifies the responses your server needs to provide. The example below demonstrates a basic implementation of row grouping.

Row Group Server
TODO

Block Size

The useServerDataSource hook accepts a blockSize parameter that determines the size of each data block requested from the server. The default value is 100 rows per block, but depending on your users' internet connections, a blockSize of 1000 is often appropriate for better performance.

Column Pivoting

Column pivoting is supported through the columnPivotsFetcher property on the useServerDataSource hook. This async callback should request column pivot definitions for the current configuration. The data source automatically calls this function when column pivots are activated in LyteNyte Grid, with all other functionality working normally.

Server Side Considerations

The server data source is the most complex but also the most flexible data source type. Your server can return any row configuration, and it's responsible for applying filter models, sort models, and aggregation models based on the current grid state.

Some important limitations to keep in mind:

  • Row selection is supported, but "select all rows" is not possible since the grid cannot know all rows at once
  • Cell selection works, but "select all cells" is not possible for similar reasons
  • Data export functions only export data that has been loaded in the UI—if you need comprehensive export capabilities, implement them server-side with dedicated API endpoints
  • Editing row data is not supported by the LyteNyte Grid server data source, as the grid doesn't hold the source of truth for the data