useLocalDataSource
Create an in-memory Docyrus-compatible data source from a JSON array, with local CRUD and Docyrus-style query parameters.
Installation
pnpm dlx @docyrus/cli add @docyrus/hooks-use-local-data-sourcepnpm add jsonataOverview
useLocalDataSource turns a JSON array of objects into a local LocalDataSource object that behaves like a lightweight Docyrus data source. It is useful for prototypes, offline demos, local previews, and components that should run without a backend.
It supports:
- Local CRUD:
list,get,create,update,updateMany,delete,deleteMany. - Docyrus-compatible field metadata, JSON Schema metadata, or inferred fields from row values.
- Local query payloads modeled after
GET /apps/:appId/data-sources/:dataSourceId/items. filterKeyword, nestedfilters,orderBy,limit,offset,columns,distinctColumns,calculations, and basicpivot.- Local formula columns using JSONata expressions.
childQueries are accepted in the query type but intentionally skipped.
Usage
'use client';
import { useState } from 'react';
import { useLocalDataSource } from '@docyrus/ui/library/hooks/use-local-data-source';
type Deal = {
id: string;
account: string;
stage: string;
amount: number;
probability: number;
};
const fields = [
{ slug: 'id', name: 'ID', type: 'field-identity', readOnly: true },
{ slug: 'account', name: 'Account', type: 'field-text' },
{ slug: 'stage', name: 'Stage', type: 'field-select' },
{ slug: 'amount', name: 'Amount', type: 'field-money' },
{ slug: 'probability', name: 'Probability', type: 'field-percent' },
{ slug: 'weighted_amount', name: 'Weighted Amount', type: 'field-money', readOnly: true }
];
export function LocalDeals() {
const [rows, setRows] = useState<Array<Deal>>([
{ id: '1', account: 'Northwind Labs', stage: 'Proposal', amount: 120000, probability: 0.6 },
{ id: '2', account: 'Contoso Finance', stage: 'Discovery', amount: 80000, probability: 0.35 }
]);
const deals = useLocalDataSource<Deal>({
id: 'local-deals',
slug: 'deals',
appSlug: 'local',
name: 'Local Deals',
data: rows,
metadata: fields,
onDataChange: setRows
});
async function loadPipeline() {
const response = await deals.list({
columns: 'stage',
formulas: {
weighted_amount: { jsonata: 'amount * probability' }
},
calculations: [
{ field: 'id', func: 'count', name: 'deals' },
{ field: 'weighted_amount', func: 'sum', name: 'weighted_pipeline' }
],
orderBy: 'weighted_pipeline DESC'
});
console.log(response.data, response.meta.total);
}
return <button onClick={loadPipeline}>Load pipeline</button>;
}Metadata
Docyrus fields
Pass an array of field-like objects when you already know the Docyrus field shape:
const metadata = [
{ slug: 'name', name: 'Name', type: 'field-text' },
{ slug: 'amount', name: 'Amount', type: 'field-money' },
{ slug: 'closed_on', name: 'Closed On', type: 'field-date' }
];JSON Schema
Pass a JSON Schema object and field types are mapped automatically:
const metadata = {
type: 'object',
properties: {
name: { type: 'string', title: 'Name' },
email: { type: 'string', format: 'email' },
amount: { type: 'number' },
active: { type: 'boolean' },
status: { type: 'string', enum: ['Open', 'Closed'] }
}
};Inferred fields
If metadata is omitted, fields are inferred from all row keys. Values are sampled to choose simple Docyrus field types such as field-text, field-number, field-checkbox, field-date, field-dateTime, field-email, field-url, and field-multiSelect.
Query Support
list(params) returns { data, meta: { total } }. meta.total is always the number of rows after formulas, keyword search, filters, calculations/pivot, sorting, and before limit/offset.
| Parameter | Description |
|---|---|
columns | Comma-separated field slugs. Used for projection and grouping when calculations are present. |
filterKeyword | Case-insensitive search across serialized row values. |
filters | Nested and/or filter groups with common Docyrus/querybuilder operators. |
formulas | Map of virtual column aliases to JSONata expressions. |
calculations | Aggregations over the filtered row set. Supports count, sum, avg, min, max, jsonb_agg, json_agg, and array_agg. |
pivot | Basic cross-tab output using the first matrix definition. |
orderBy | String, object, or array sort definition. |
limit / offset | Local pagination. |
distinctColumns | Dedupe rows by one or more columns before projection. |
Filters
Supported operators include equality/inequality, comparisons, text matching, set membership, ranges, null/empty checks, and their negated variants:
await source.list({
filters: {
combinator: 'and',
rules: [
{ field: 'stage', operator: 'notIn', value: ['Closed Lost'] },
{ field: 'amount', operator: '>=', value: 50000 },
{
combinator: 'or',
rules: [
{ field: 'region', operator: '=', value: 'EMEA' },
{ field: 'region', operator: '=', value: 'APAC' }
]
}
]
}
});JSONata formulas
Each formula runs against the current row and writes a virtual column with the formula key:
await source.list({
formulas: {
weighted_amount: { jsonata: 'amount * probability' },
display_name: { jsonata: 'account & " · " & stage' }
},
columns: 'account, weighted_amount, display_name'
});API Reference
useLocalDataSource(options)
| Option | Type | Default | Description |
|---|---|---|---|
data | Array<TData> | — | Source rows. Required. Rows without id receive a generated id from getRowId. |
metadata | LocalDataSourceMetadata | inferred | Docyrus field list or JSON Schema. |
id | string | 'local-data-source' | Data source id exposed through dataSource.id. |
slug | string | 'local' | Data source slug. |
appSlug | string | 'local' | App slug used by the local client shim. |
name | string | 'Local Data Source' | Display name. |
getRowId | (row, index) => string | index + 1 | Generates ids for rows that do not already have an id. |
onDataChange | (data) => void | — | Called after local create/update/delete operations. Use this to keep React state in sync. |
Return Value
| Property | Type | Description |
|---|---|---|
dataSource | DataSource | Docyrus-compatible data source metadata. |
fields | Array<DataSourceField> | Normalized field list. |
getData | () => Array<TData> | Return the current local rows. |
setData | (data) => void | Replace all rows and call onDataChange. |
list | (params?) => Promise<{ data, meta: { total } }> | Query rows with local Docyrus-style query params. |
get | (id) => Promise<TData | undefined> | Read one row by id. |
create | (record) => Promise<TData> | Insert a row and generate an id when needed. |
update | (id, patch) => Promise<TData> | Patch one row. Throws if the id does not exist. |
updateMany | ({ records }) => Promise<Array<TData>> | Bulk patch rows by id. Matches the collection shape used by useDocyrusDataGrid. |
delete | (id) => Promise<void> | Delete one row. |
deleteMany | ({ recordIds }) => Promise<void> | Bulk delete rows by id. |
client | RestApiClient | Local client shim used by useLocalDataSourceDataGrid for metadata/view requests. |
createLocalDataSource
createLocalDataSource(options) exposes the same local data source object without using React. Use it in tests, examples, or non-React local query helpers.
useDynamicFormView
Backend-agnostic dynamic form renderer — the sibling of useDocyrusFormView with no Docyrus wiring. Supply fields, values, options, and an onSubmit sink to render create/edit/view forms against any backend.
useLocalDataSourceDataGrid
Wire a LocalDataSource object into the Docyrus DataGrid hook stack, including toolbar search, filters, sorting, grouping, saved views, and local CRUD.