Hooks

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-source
Required Packages(1 package)
pnpm add 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

'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.

ParameterDescription
columnsComma-separated field slugs. Used for projection and grouping when calculations are present.
filterKeywordCase-insensitive search across serialized row values.
filtersNested and/or filter groups with common Docyrus/querybuilder operators.
formulasMap of virtual column aliases to JSONata expressions.
calculationsAggregations over the filtered row set. Supports count, sum, avg, min, max, jsonb_agg, json_agg, and array_agg.
pivotBasic cross-tab output using the first matrix definition.
orderByString, object, or array sort definition.
limit / offsetLocal pagination.
distinctColumnsDedupe 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)

OptionTypeDefaultDescription
dataArray<TData>—Source rows. Required. Rows without id receive a generated id from getRowId.
metadataLocalDataSourceMetadatainferredDocyrus field list or JSON Schema.
idstring'local-data-source'Data source id exposed through dataSource.id.
slugstring'local'Data source slug.
appSlugstring'local'App slug used by the local client shim.
namestring'Local Data Source'Display name.
getRowId(row, index) => stringindex + 1Generates 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

PropertyTypeDescription
dataSourceDataSourceDocyrus-compatible data source metadata.
fieldsArray<DataSourceField>Normalized field list.
getData() => Array<TData>Return the current local rows.
setData(data) => voidReplace 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.
clientRestApiClientLocal 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.

On this page