AG Studio Launch Week 🚀🚀🚀 28 Sep - 2 Oct 2026 🚀🚀🚀 Join now

JavaScript Embedded AnalyticsLoading Data

Version 3.0.0

Data is loaded into the built-in engine one of two ways: synchronously, from data already held in memory, or asynchronously, fetched on demand from a database, warehouse, or API.

Sync Data Copy Link

Sync data sources can be used when data has already been loaded in the application.

A sync data source represents a single table of data.

const studioProperties = {
    data: {
        sources: [{
            id: 'medals',
            data: [
                {
                    year: 2000,
                    sport: 'Swimming',
                    country: 'United States',
                    // ... other fields
                },
                // ... other rows
            ],
        }],
    },

    // other studio properties ...
}

Sync data sources are represented by the AgSimpleDataSourceDefinition interface.

string
Table ID
string
Table display name. If not provided, a formatted version of id will be used.
descriptionCopy Link
string
AI-facing description of this table's contents and purpose.
TData[]
Row data.
fieldsCopy Link
AgFieldDefinition<TRegistry, any, AgFormat<TRegistry>, any, any>[]
Fields in the table. If not provided, will be inferred from the data.

Fields Copy Link

By default, if no fields are provided, they will be inferred from the data.

It is also possible to provide and customise fields as part of the source definition.

The example above demonstrates customising fields. The country field has been titled Location, and the age field has been hidden from the UI.

string
Field ID.
string
Display name.
descriptionCopy Link
string
Field description. Displayed in the Field Panel
boolean
Set to true to hide from being selected in the UI. Field can still be used for joins.
editableCopy Link
boolean | AgFieldEditableKey[]
Controls whether the field can be edited in the UI.
serializerCopy Link
AgFieldSerializer<InferDataTypeFromFormat<TRegistry, TFormat>>
Optional. How the field values will be serialized into state. Defaults to format serializer.
deserializerCopy Link
AgFieldDeserializer<InferDataTypeFromFormat<TRegistry, TFormat>>
Optional. How the field values will be deserialized from state. Defaults to format deserializer.
createValueFormatterCopy Link
AgFieldValueFormatterFactory<InferDataTypeFromFormat<TRegistry, TFormat>, TFormatOptions, any>
Optional. Build a value formatter bound to the field's format options and the runtime API. Defaults to format factory.
blankValueCopy Link
string
Optional. How blank values will be displayed. Defaults to format blank value.
formatOptionsCopy Link
TFormatOptions
Optional. Will be passed to the value formatter.
contextCopy Link
TFieldContext
Optional. An application-defined object carried on the hydrated field, and passed back on that field to callbacks such as a grid widget's createCellRenderer. Studio never reads it. Replaces any context set on the field's format.
formatCopy Link
TFormat
The format type of the field (provides default formatting, etc.).
accessorCopy Link
AgFieldDataAccessor<TData, InferDataTypeFromFormat<TRegistry, TFormat>>
Optional. How to retrieve the value from the data. Either the property key, or a callback. If undefined, id will be used as the property key.
cardinalityCopy Link
AgFieldCardinality
Optional. Cardinality of the field data. Improves performance if provided.
notBlankCopy Link
boolean
Optional. Does the field contain blank values. Improves performance if provided.
supportedBucketsCopy Link
string[]
Optional. The buckets that this field supports. If undefined, will default to the supportedBuckets on the format.

Reloading Sync Data Copy Link

Sync data can be reloaded by passing updated data sources to the data property.

Note that only the data will be updated. Data sources cannot be added or removed, and fields cannot be updated.

Async Data Copy Link

Async data sources lazy load data on demand. This is the usual choice when your data lives in a database, a data warehouse, or behind an API, since Studio defers loading until a widget needs the data. Reducing how many rows cross the wire additionally requires the source to declare filter, sort or pagination support, described in Data Source Filtering, Sorting, and Pagination below.

An async data source represents one or more tables of data.

Async data sources still run entirely in the built-in engine: Studio fetches rows through getData and processes them in the browser. If your dataset is too large to fetch and process this way, or you want your own backend to execute queries directly, see Custom Engine instead.

const studioProperties = {
    data: {
        sources: [{
            id: 'medalsSource',
            dataShape: 'row',
            getData: async (tableId) => {
                const data = await fetchData(tableId);
                return { data };
            },
            tables: [
                {
                    id: 'medals',
                    fields: [
                        {
                            id: 'athlete',
                            format: 'textFormat',
                        },
                        // ... other fields
                    ],
                },
                // ... other tables
            ],
        }],
    },

    // other studio properties ...
}

Async data sources can return row-based or column-based data. This is determined by the dataShape property.

Properties available on the AgDataSourceDefinition<TDataShape extends AgDataShape, TRegistry extends AgBaseRegistry = AgDefaultRegistry> interface.

string
Data source ID
string
Data source display name. If not provided, a formatted version of id will be used.
getDataCopy Link
Function
Callback to return the data for the provided table and fields. The optional options parameter carries a paging row-window hint, a filter tree, and a sort order. The engine only includes paging when the source declares capabilities.pagination: true, and only includes filter/sort when the source declares capabilities.filter: true/capabilities.sort: true respectively - independently of capabilities.pagination. Sources that do not declare a given capability receive no corresponding option and should return all data for that aspect - the engine applies client-side pagination, filtering, and sorting as a fallback.
dataShapeCopy Link
TDataShape
'row' if the data is row-based, or 'column' if the data is column-based.
tablesCopy Link
AgAsyncTableDefinition<TRegistry>[]
One or more tables that are provided by this data source.
capabilitiesCopy Link
AgDataSourceCapabilities
Static capability declaration. Tells the engine whether the getData callback supports server-side pagination. When omitted, the engine treats the source as having no server-side capabilities and applies client-side pagination as a fallback.
pageSizeCopy Link
number
Maximum number of rows the engine requests from this source in a single getData call. Set this when the source has a real per-call limit it cannot exceed (for example, a REST API with its own hard page-size cap). Whenever the engine needs more rows than this from the source - including an unrestricted "fetch everything" request - it issues multiple getData calls of at most this many rows each and assembles the combined result, rather than trusting a single call to return more than the source can actually serve. Only has an effect when capabilities.pagination is true. Applies to both row-shape and column-shape sources - for a column-shape source, each field's column is fetched and reassembled in pageSize-sized chunks the same way row data is.

Reloading Async Data Copy Link

Async data can be reloaded by calling api.reload().

Data Source Filtering, Sorting, and Pagination Copy Link

A source declares capabilities.filter, capabilities.sort, and capabilities.pagination independently of each other - any combination of the three can be set. A source without capabilities.pagination is still re-fetched in full whenever the applied filter or sort changes, rather than served from a single cached fetch, so declaring filter/sort there still avoids evaluating that criteria locally, just without the row-window benefit pagination also provides.

The example below simulates a server-side endpoint that handles all three. Its request log prints the exact filter, sort order and row window sent to the fake server on every call.

The example starts with a page filter on Region already applied, so the log shows an unfiltered fetch of all 240 rows and then the filtered one, which the server narrows to 120 before returning anything. The filter arrives as a tree naming the field and operator, not as pre-resolved rows. Change it in the Filters panel, or add one on Amount, to watch the requests change.

The source also declares a pageSize of 100, so each of those fetches is split into consecutive calls with advancing row windows rather than one request for the whole result.

The grid in this example groups by Region and Product and aggregates Amount and Units, so it sorts and pages that grouped result locally: the log reports sort=none however the grid is sorted, and the row windows it does show come from pageSize splitting one full fetch. That is what a grouping query does, not a missing sort - see Sort Pushdown below.

Filter Pushdown Copy Link

By default, filters applied in AG Studio are evaluated locally after fetching all the data. A source that sets capabilities.filter: true receives the applied filter as part of getData's options, and can filter the data itself before returning it.

{
    capabilities: { filter: true },
    getData: async (tableId, fieldIds, options) => {
        const rows = await fetchRows(tableId, options?.filter);
        return { data: rows };
    },
}

A filter on an aggregated value (a measure, or a total) is always evaluated locally as the aggregates they filter on are only available locally.

Sort Pushdown Copy Link

By default, sorting applied in AG Studio is evaluated locally after fetching all the data. A source that sets capabilities.sort: true receives the applied sort order as part of getData's options, and can sort the data itself before returning it.

{
    capabilities: { sort: true },
    getData: async (tableId, fieldIds, options) => {
        const rows = await fetchRows(tableId, options?.sort);
        return { data: rows };
    },
}

Sort pushdown, and the row-window benefit of pagination, apply to a query that reads source rows directly. Where a query groups or aggregates its fields, it resolves its sort order and row window over that computed result instead, and both are applied locally whatever the source declares.

Whether a particular widget's query groups or aggregates depends on how that widget is configured, so read the requests your own source receives to see which of them carry a sort or a paging window.

Pagination Copy Link

By default, a query's full result set is fetched in one call and paged through locally. A source that sets capabilities.pagination: true instead receives a paging.offset/paging.pageLimit window as part of getData's options, and is expected to return exactly that window - the engine trusts the response as-is, with no local re-slicing, other than truncating a response that returns more rows than the requested pageLimit.

{
    capabilities: { pagination: true },
    pageSize: 500,
    getData: async (tableId, fieldIds, options) => {
        const { offset = 0, pageLimit } = options?.paging ?? {};
        const rows = await fetchRows(tableId, offset, pageLimit);
        return { data: rows };
    },
}

Set pageSize on the source when it has its own per-call row limit (a REST API's page-size cap, for example). The engine then splits any request for more rows than pageSize - including an unbounded "fetch everything" request - into multiple getData calls of at most pageSize rows each, and assembles the combined result. pageSize only has an effect alongside capabilities.pagination: true.

A response with fewer rows than requested is always read as "no more data after this point".

Multiple Tables Copy Link

When multiple tables are provided, whether sync or async, they can be linked by providing Relationships.