Docyrus Query Builder
Author Docyrus data-source queries on a phone — columns tree, nested filters, sort, pagination, calculations, formulas, child queries and pivots, with a live Run preview and the JSON payload.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-docyrus-query-builderpnpm add @react-querybuilder/core react-native-reanimated react-native-gesture-handler @shopify/flash-list tailwind-variantsDocyrusDataSourceQueryBuilder is the React Native port of the web component with the same name. It uses the same query model (ISelectQueryParams) and the same hook (useDocyrusQueryBuilder). Its section editors produce the same payload for the Docyrus items endpoint (/v1/apps/{app}/data-sources/{ds}/items), so a query written on a phone can be saved and run on the web without changes, and the other way round.
The layout is built for mobile:
- Primary tabs: Configure, Preview and JSON.
- Configure shows one row per section with a live summary, such as "Filters · 3 rules" or "Columns · 12 selected". Tapping a row opens that section in a full-height bottom sheet (
presentation="sheet", the default) or inline with a back row ("stack"). You can also push your own screen fromonSectionPress. - Preview calls
runQuery()and shows the returned rows in a horizontally scrolling read-only table, with the row count, the total and any error. - JSON shows the cleaned payload read-only in a monospace font, in pretty or compact form. It has a Copy button when you pass
onCopy. The component adds no clipboard dependency, so wireexpo-clipboardor a similar library yourself.
Every section except Select Data Source stays disabled until a data source is selected.
Works without a client
When you pass dataSources, and optionally fields, but no client, every configure section still works offline. Only Run needs a client: without one, it reports that authentication is required. With a client, the builder:
- discovers data sources (
/v1/apps/data-sources) - loads the selected source's schema including enums (
/v1/apps/{app}/data-sources/{ds}?expand=enums) - loads user, team, role and org-unit filter options (
/v1/users,/v1/teams,/v1/users/acl/roles,/v1/users/acl/hierarchy-units) - loads relation filter pickers (
…/items?columns=id,name) - loads parent and child field groups (
/v1/dev/data-sources/:id/fields?expand=parent,child) - runs the query (
/items)
Usage
import { useState } from 'react';
import * as Clipboard from 'expo-clipboard';
import { useDocyrusClient } from '@docyrus/signin/react-native';
import {
DocyrusDataSourceQueryBuilder,
type ISelectQueryParams
} from '@/components/docyrus-native/docyrus-query-builder';
export function QueryScreen() {
const client = useDocyrusClient();
const [value, setValue] = useState<Partial<ISelectQueryParams>>({});
return (
<DocyrusDataSourceQueryBuilder
client={client}
value={value}
onChange={setValue}
enableCalculations
enableChildQueries
onCopy={json => void Clipboard.setStringAsync(json)}
className="flex-1" />
);
}Offline, with a fixed data source
<DocyrusDataSourceQueryBuilder
value={{ dataSourceId: 'ds-contact', dataSourceFullSlug: 'crm.contact', limit: 25 }}
onChange={setValue}
dataSources={[contactDataSource]}
lockDataSource
presentation="stack" />Pushing sections as screens
When onSectionPress returns true, the builder does not open the section itself. Render the matching section editor on your own screen inside a DSQBContext provider that holds the builder's hook result:
const dsqb = useDocyrusQueryBuilder({ value, onChange, client });
<DSQBContext value={dsqb}>
<DSQBFiltersEditor />
</DSQBContext>Sections
| Section | Web counterpart | Native editor |
|---|---|---|
| Select Data Source | Two panes: grouped list and field table | A searchable list grouped by app, with collapsible groups (search opens every group), plus a card for the selected source whose field list you can expand. Hidden by lockDataSource. Picking a source closes the sheet. |
| Columns | TreeView multi-select | The native TreeView with checkboxes. Leaf ids are paths (status|id → status(id), company|owner|email → company(owner(email))). User and enum fields expand into fixed sub-field folders. The Parent Data Sources switch nests a parent's fields under the relation column that points to it. Shows the generated columns string. |
| Filters | react-querybuilder QueryBuilderDocyrus | The native QueryBuilderDocyrus, driven by getOperatorsForFieldType, user / team / role / unit option sources (createDocyrusOperatorValueConfig) and relation record pickers, plus the full-text filterKeyword. The id-bearing rule tree is kept locally and rebuilt only when value.filters changes from outside (web #238). |
| Sort | Field selector, ASC/DESC, drag handle | Field selector sheet, ASC/DESC toggle, Move up / Move down and remove |
| Pagination | Limit (with presets), offset, full count | Same controls: number inputs, preset chips and a fullCount switch |
Calculations (enableCalculations) | Aggregate rows with an Advanced collapsible | Aggregate Select, field, alias, and an Advanced section (distinct, min/max, number type), with move up/down |
Formulas (enableFormulas) | Nested block cards with drag | Nested block cards with a colour chip per kind and a coloured left border. Each card has kind, cast and tz controls. Add / insert first / remove / move up / move down replace drag. Inline or subquery formulas (from / with). Formula keys are renamed when editing ends. |
Child Queries (enableChildQueries) | Child data-source picker and per-entry cards | Same flow. Opening the section turns on relatedExpand.child. value.childQueries is an array, and each entry's alias is injected into the parent columns (the items endpoint drops the child block without it) and removed from them again when the entry is deleted. Each card shows from / using read-only, a columns tree, order by, direction and limit. |
Pivot (enablePivot) | Matrix entries | Matrix entry cards (using, columns, spread, and an optional date range typed as ISO text), move up/down, plus hideEmptyRows / limit |
API Reference
DocyrusDataSourceQueryBuilderProps
| Prop | Type | Default | Description |
|---|---|---|---|
value | Partial<ISelectQueryParams> | - | Required. The current query payload (controlled). |
onChange | (value: Partial<ISelectQueryParams>) => void | - | Required. Receives the next payload. dataSourceId, dataSourceFullSlug and showGroupSummaries are removed from it. |
client | DocyrusDataSourceQueryBuilderClient | null | null | An authenticated client, which only needs get. It enables discovery, filter option sources, related fields and Run. |
fields | IField[] | [] | Fallback field list, used when the selected source has no fields. |
dataSources | IDataSourceReference[] | [] | Static data sources. Used when there is no client, and as a fallback when discovery fails. |
locale | UiI18nLocale | 'en' | Locale exposed on the context. |
size | 'sm' | 'default' | 'lg' | 'default' | Text size of section titles and summaries. |
variant | 'default' | 'bordered' | 'compact' | 'default' | Visual style. |
className | string | - | Root container className. The root is flex-1, so give it a height. |
defaultSection | DSQBSection | - | Section to open on mount (as a sheet or inline). Leave it out to start on the section list. |
lockDataSource | boolean | false | Fixes the data source to the one in value: hides the data-source section and the clear button. |
enableCalculations | boolean | false | Shows the Calculations section. |
enableFormulas | boolean | false | Shows the Formulas section. |
enableChildQueries | boolean | false | Shows the Child Queries section. |
enablePivot | boolean | false | Shows the Pivot section. |
presentation | 'stack' | 'sheet' | 'sheet' | 'sheet' opens a tapped section in a full-height bottom sheet. 'stack' shows it inline, in place of the list, with a back row. |
onSectionPress | (section: DSQBSection) => boolean | void | - | Called when a section row is tapped. Return true to handle navigation yourself. |
onCopy | (json: string) => void | - | Copy handler for the JSON tab. The Copy button is hidden when this is omitted. |
filterControlElements | Partial<QBControlElements> | - | Control-element overrides for the Filters QueryBuilderDocyrus. They are merged over the defaults. Pass a stable reference. |
renderFilterValueEditor | (props: ValueEditorProps, context: unknown) => ReactNode | undefined | - | Replaces the value editor of a rule. Return undefined to keep the default editor. Ignored when filterControlElements.valueEditor is set. |
filterContext | unknown | - | Passed to renderFilterValueEditor as its second argument. |
DSQBFieldSelector
| Prop | Type | Default | Description |
|---|---|---|---|
fields | IField[] | - | Fields to pick from. They are grouped by category (text, numeric, date, select, relation, user, boolean, other). |
value | string | - | Slug of the selected field. |
onSelect | (slug: string) => void | - | Called when a field is picked. |
placeholder | string | 'Select field...' | Text on the trigger when nothing is selected. |
title | string | placeholder | Title of the sheet. |
size | 'sm' | 'default' | 'lg' | 'default' | Size of the trigger. |
showTypeIcon | boolean | true | Shows the columns icon on the trigger. |
disabled | boolean | false | Disables the trigger. |
className | string | - | className for the trigger. |
DSQBJsonPreview
| Prop | Type | Default | Description |
|---|---|---|---|
value | Partial<ISelectQueryParams> | - | Query payload. It is cleaned with sanitizeSelectQueryParams and cleanPayload before display. |
onCopy | (json: string) => void | - | Shows the Copy button. |
className | string | 'flex-1 gap-2' | Container className. |
DSQBFormulaBlock
| Prop | Type | Default | Description |
|---|---|---|---|
block | IQueryFormulaBlock | - | The block node. |
onChange | (block: IQueryFormulaBlock) => void | - | Replaces the node. |
onRemove | () => void | - | Shows the remove button. |
onMoveUp / onMoveDown | () => void | - | Native only. Shows the move buttons, which replace drag. |
fields | Array<{ slug: string; name: string }> | - | Fields offered by column blocks. |
depth | number | 0 | Nesting depth, used for indentation. |
The other section editors take no props. They read everything through useDSQB().
useDocyrusQueryBuilder(options)
It takes UseDocyrusQueryBuilderOptions: value, onChange, client, fields, dataSources, locale and size. It returns UseDocyrusQueryBuilderResult. The hook uses no React Query.
| Field | Type | Description |
|---|---|---|
value / updateValue(patch) | Partial<ISelectQueryParams> / (patch) => void | Current payload, and a patch writer that sanitizes the result. |
fields | IField[] | The selected source's fields, or the fields option as a fallback. |
filterOptionSources | DocyrusQueryBuilderOptionSources | Users, teams, roles and units for filter pickers. |
relationValuesByField | Record<string, DocyrusQueryBuilderOption[]> | Related records (id and name) for each relation field slug. |
dataSources, dataSourcesStatus, dataSourcesError, reloadDataSources() | — | Discovery state. |
selectedDataSource, selectDataSource(ds), unselectDataSource() | — | Selection. Selecting a source replaces the payload with { dataSourceId, dataSourceFullSlug, limit: 25 }. Unselecting clears it. |
dataSourceDetailStatus, dataSourceDetailError, reloadSelectedDataSource() | — | Detail fetch with enums. |
ensureDataSourceFields(id) | (id: string) => Promise<IField[]> | Lazy-loads the schema of a related source. |
fetchRelatedFields(expand) | (expand: Array<'parent' | 'child'>) => Promise<DSQBRelatedFields> | Parent and child field groups. |
relatedExpand / setRelatedExpand | DSQBRelatedExpand / Dispatch<SetStateAction<…>> | Expansion state kept in the hook. The fetch is de-duplicated on a <dataSourceId>::<expand> key, and a failed attempt stays pinned to that key. |
relatedFields, relatedFieldsLoading | — | The resolved groups. |
preview, runQuery() | DSQBPreviewState / () => Promise<void> | Run state. |
locale, size | — | Echoed from the options. |
Context
DSQBContext and useDSQB() hold a DSQBContextValue: the hook result plus filterControlElements, renderFilterValueEditor and filterContext. useDSQB() throws when it is called outside the builder.
Components
| Component | Description |
|---|---|
DocyrusDataSourceQueryBuilder | Root: the Configure / Preview / JSON tabs, the section list and the sheets |
DSQBDataSourceSelector | Data-source section |
DSQBColumnsEditor | Columns tree section |
DSQBFiltersEditor | Filters section (keyword and QueryBuilderDocyrus) |
DSQBOrderByEditor | Sort section |
DSQBPaginationEditor | Pagination section |
DSQBCalculationsEditor | Calculations section |
DSQBFormulaEditor / DSQBFormulaBlock | Formulas section and a single block node |
DSQBChildQueriesEditor | Child Queries section |
DSQBPivotEditor | Pivot section |
DSQBRunPreview | Run and result table |
DSQBJsonPreview | JSON payload view |
DSQBFieldSelector | Searchable field picker in a sheet |
Hooks, constants & utilities
| Export | Description |
|---|---|
useDocyrusQueryBuilder, useDSQB, DSQBContext | Hook and context |
createDocyrusOperatorValueConfig(fieldType, sources) | Per-operator value options and editor types for user and follower fields |
loadDocyrusQueryBuilderOptionSources(client) | Fetches users, teams, roles and units |
docyrusQueryBuilderVariants | tv() slot styles (variant / size) |
FILTER_OPERATOR_CATEGORIES, ALL_FILTER_OPERATORS | Filter operator catalog |
FUNCTION_CATEGORIES, ALL_ALLOWED_FUNCTIONS, ALLOWED_AGGREGATES, ALLOWED_CAST_TYPES, DATE_FORMULAS | Allowed functions, aggregates, casts and date formulas |
BUILTIN_NAMES, EXTRACT_PARTS, MATH_OPS, COMPARE_OPS, BOOLEAN_OPS | Formula operators |
BLOCK_KINDS, BLOCK_KIND_LABELS, BLOCK_KIND_COLORS | Formula block kinds |
QUERY_MODES, QUERY_MODE_DESCRIPTIONS, FILTER_TYPES, NUMBER_TYPES, DATE_RANGE_INTERVALS | Enumerations |
parseColumnString, serializeColumns | Column DSL parser and serializer (alias:func[args]@field(sub), ...spread) |
normalizeOrderBy, serializeOrderBy | Order-by normalization |
buildDefaultColumns, cleanPayload | Payload helpers |
getFieldTypeCategory, getOperatorsForFieldType | Field-type helpers |
validateQuery(params, fields) | Validation errors and warnings. On native, a filter rule on a field that is not in a non-empty fields list is reported as a warning. |
countFilterRules | Counts leaf rules |
createBlock, updateBlockAt, removeBlockAt, insertBlockAt, getBlockLabel | Formula-block tree helpers |
prepareRuleGroup, toRuleGroup, fromRuleGroup | Native only. Local id-stamping and the IQueryFilterGroup ⇄ rule-tree bridge, so the component does not import react-querybuilder |
Type Exports
| Type | Description |
|---|---|
DocyrusDataSourceQueryBuilderProps | Root props |
DocyrusDataSourceQueryBuilderClient | { get(path, params?) } |
UseDocyrusQueryBuilderOptions / UseDocyrusQueryBuilderResult / DSQBContextValue | Hook and context |
DSQBPrimaryTab / DSQBSection / DSQBPresentation | 'configure' | 'preview' | 'json', the section ids, 'stack' | 'sheet' |
DSQBFilterValueEditorRenderer | Type of renderFilterValueEditor |
DSQBPreviewState | { status, rows, columns, total?, error?, raw? } |
DocyrusQueryBuilderOption / DocyrusQueryBuilderOptionSources | Filter picker options |
ISelectQueryParams | Full query payload |
IQueryFilterGroup / IQueryFilterRule / QueryFilterType | Filters |
ISelectQueryOrderBy / ISelectQueryCalculationRule / AggregateFunction / NumberType | Sort and calculations |
IQueryFormula / IQueryBlockInlineFormula / IQueryBlockSubqueryFormula / IQueryFormulaBlock / BlockKind | Formulas |
IBlockLiteral … IBlockBoolean, BuiltinName, ExtractPart, MathOp, CompareOp, BooleanOp | Block AST |
IQueryChildQueryParams | A child query entry (alias, from, using, columns?, filters?, calculations?, orderBy?, limit?) |
ISelectPivot / ISelectPivotMatrixQuery / DateRangeInterval | Pivot |
QueryMode / ExpandType | Enumerations |
IDataSourceReference | A data source with fields |
DSQBParentFieldGroup / DSQBChildFieldGroup / DSQBRelatedFields / DSQBRelatedExpand | Related field groups |
IQueryValidationError / ParsedColumn | Validation and column DSL |
IField / IFieldType / UiI18nLocale | Field model |
ValueEditorType / DSQBRuleType / DSQBRuleGroupType | Native only. Local structural react-querybuilder types |
Differences from web
- Removed:
aiAssistantOpen,onAiAssistantOpenChange,renderAiAssistant,useApplyQuery,IQueryBuilderAiAssistantRenderContextand the Cody agent toggle. - Added:
presentation,onSectionPress,onCopy,renderFilterValueEditor. - Changed:
filterControlElementsuses the nativeQBControlElementsslot map. The web version uses react-querybuilder'sControlElementsProp.filterContextgoes torenderFilterValueEditor, because the native builder has nocontextprop. - Changed:
defaultSectiondefaults to no open section. The web version always shows one section. - Replaced interactions: Drag handles in Sort and Formulas become move up / move down buttons. Pivot date-range bounds are ISO text inputs instead of
datetime-local.
Translations
All copy goes through useUiTranslation() under ui.docyrusQueryBuilder.*, with English fallbacks. The web component hard-codes English, so these keys are new: selectDataSource, columns, filters, sort, pagination, calculations, formulas, childQueries, pivot and their *Description keys, configure, preview, run, runQuery, running, searchFields, selectField, parentDataSources, allColumnsReturned, selected, keyword, filterRules, addSortRule, limit, offset, fullCount, addCalculation, advanced, distinct, addFormula, addInput, addWhen, addChildDataSource, addMatrixEntry, moveUp, moveDown, remove, copy, copied, pretty, compact, jsonOutput, noRows, runError, selectDataSourceFirst, the category* field-group labels and the empty or loading state messages.
DocyrusIcon
CDN-powered icon component supporting Font Awesome, Huge Icons, emojis and custom libraries, with an SVG cache, a same-footprint loader and looping animations.
DummyDataGenerator
Inline three-step wizard (Configure → Preview → Result) that generates realistic sample records per field type with a seeded generator, previews them with the value renderers, exports CSV / Excel / JSON / Markdown through the share sheet, and saves them to a data source. API-aligned with the web DummyDataGenerator.