# useLocalDataSource URL: /docs/web/hooks/use-local-data-source Create an in-memory Docyrus-compatible data source from a JSON array, with local CRUD and Docyrus-style query parameters. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/hooks-use-local-data-source ``` **Dependencies:** - [jsonata](https://www.npmjs.com/package/jsonata) ## Overview `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`, nested `filters`, `orderBy`, `limit`, `offset`, `columns`, `distinctColumns`, `calculations`, and basic `pivot`. - Local formula columns using JSONata expressions. `childQueries` are accepted in the query type but intentionally skipped. ## Usage ```tsx '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>([ { 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({ 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 ; } ``` ## Metadata ### Docyrus fields Pass an array of field-like objects when you already know the Docyrus field shape: ```ts 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: ```ts 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: ```ts 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: ```ts 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` | — | 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` | Normalized field list. | | `getData` | `() => Array` | 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` | Read one row by `id`. | | `create` | `(record) => Promise` | Insert a row and generate an id when needed. | | `update` | `(id, patch) => Promise` | Patch one row. Throws if the id does not exist. | | `updateMany` | `({ records }) => Promise>` | Bulk patch rows by id. Matches the collection shape used by `useDocyrusDataGrid`. | | `delete` | `(id) => Promise` | Delete one row. | | `deleteMany` | `({ recordIds }) => Promise` | 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.