Protocol reference
The protocol is a small, framework-neutral JSON contract. It is defined once as
JSON Schema and mirrored as TypeScript types (@elyra/datagrid-protocol) and PHP
DTOs (elyra/datagrid-protocol).
- Schemas:
packages/protocol/schema/grid-request.schema.json,grid-response.schema.json - TypeScript:
import type { GridRequest, GridResponse } from '@elyra/datagrid-protocol' - PHP:
Elyra\DataGrid\Protocol\{GridRequest, GridMutation}
GridRequest
interface GridRequest {
columns?: string[]; // fields to return (default: all declared)
sort?: SortSpec[]; // multi-column, applied in order
filter?: FilterGroup; // structured filter tree
search?: Search; // global search
group?: GroupSpec[]; // grouping with per-group aggregates
aggregates?: AggregateSpec[]; // footer aggregates over the whole result
view: Viewport; // required — the viewport window
meta?: ('total' | 'facets' | 'timing')[]; // opt-in extras
facets?: FacetSpec[]; // facet specs (with meta: ['facets'])
}
interface Viewport { skip: number; take: number; } // take: visible rows, ≤ 1000
interface SortSpec { field: string; dir: 'asc' | 'desc'; nulls?: 'first' | 'last'; }
Filters
interface FilterGroup { logic: 'and' | 'or'; filters: (FilterCondition | FilterGroup)[]; }
interface FilterCondition { field: string; op: FilterOp; value?: Scalar | Scalar[]; }
type FilterOp =
| 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte'
| 'in' | 'nin' | 'between'
| 'contains' | 'ncontains' | 'startsWith' | 'endsWith'
| 'isNull' | 'isNotNull';
in/nintake arrays;betweentakes[min, max];isNull/isNotNulltake no value.
Search
interface Search {
query: string;
mode?: 'hybrid' | 'fulltext' | 'substring'; // default substring
fields?: string[];
vectorField?: string; // embedding column for hybrid mode
}
Grouping & aggregates
interface GroupSpec { field: string; aggregates?: AggregateSpec[]; rollup?: boolean; }
interface AggregateSpec {
field: string;
fn: 'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'median' | 'percentile';
p?: number; // fraction in (0,1) for percentile
as?: string; // output alias
}
interface FacetSpec { field: string; top?: number; } // top-N most frequent
GridResponse
interface GridResponse {
rows: Row[]; // the viewport slice
view: Viewport; // echo of skip/take served
total?: number | null; // present if meta included 'total'
groups?: GroupRow[]; // when grouping was requested
aggregates?: Record<string, number | null>; // footer aggregates
facets?: Record<string, FacetBucket[]>; // per-field value lists
timing?: { elapsedMs?: number; backend?: string; usedIndex?: boolean | null; fullScan?: boolean | null; };
notice?: { level: 'info' | 'warn'; text: string; url?: string }; // e.g. unlicensed reminder
}
interface FacetBucket { value: Scalar; count: number; }
interface GroupRow {
field: string; value: Scalar; count: number;
level?: number; // depth in the hierarchy (0 = outermost)
path?: string; // stable key for collapse tracking
aggregates?: Record<string, number | null>;
isGrandTotal?: boolean;
}
GridMutation
type MutationOp = 'create' | 'update' | 'delete';
interface GridMutation { op: MutationOp; id?: Scalar; changes?: Record<string, unknown>; }
interface GridMutationResult {
ok: boolean;
id?: Scalar; // resulting key (e.g. after create)
row?: Row; // persisted row echoed back
errors?: Record<string, string[]>; // per-field validation errors
}
Cost model in the contract
view.takeis a viewport window, not a page size.- Expensive extras are opt-in via
meta:total(row count),facets(distinct value lists),timing. Nothing is computed unless requested.