Docyrus

Docyrus Query Builder

Visual query builder for Docyrus data sources — filters, columns, sorting, calculations, formulas, pivots, and child queries.

Client Only

Installation

pnpm dlx @docyrus/cli add @docyrus/ui-docyrus-query-builder
Required Packages(1 package)
pnpm add react-querybuilder
UI Primitives(16 components)
npx shadcn@latest add badge button checkbox collapsible command input label popover radio-group scroll-area select switch table tabs toggle-group tooltip

Usage

'use client';

import { useState } from 'react';

import {
  DocyrusQueryBuilder,
  type ISelectQueryParams
} from '@docyrus/ui/components/docyrus-query-builder';

export function MyQueryBuilder() {
  const [query, setQuery] = useState<Partial<ISelectQueryParams>>({});

  return (
    <DocyrusQueryBuilder
      value={query}
      onChange={setQuery}
    />
  );
}

With a Docyrus API client

Pass an authenticated RestApiClient from @docyrus/api-client (or any object that implements the DocyrusQueryBuilderClient shape) to enable data-source discovery and query previews.

import { useDocyrusClient } from '@docyrus/signin';

export function MyAuthedQueryBuilder() {
  const client = useDocyrusClient();
  const [query, setQuery] = useState<Partial<ISelectQueryParams>>({});

  return (
    <DocyrusQueryBuilder
      client={client}
      value={query}
      onChange={setQuery}
    />
  );
}

Without a client (offline / preset data)

Skip the client and supply dataSources and fields yourself for offline editing and form-driven workflows.

<DocyrusQueryBuilder
  value={query}
  onChange={setQuery}
  dataSources={myDataSources}
  fields={myFields}
/>

EditorAgent integration

Pair useApplyQuery with renderAiAssistant to let an LLM author Docyrus query specs. The hook owns the controlled value and ships an applyQuery tool that the agent calls to commit a new query spec — the tool guards against primitives / arrays / null so a malformed payload cannot crash the builder.

import { EditorAgent } from '@docyrus/ui/components/editor-agent';
import {
  DocyrusDataSourceQueryBuilder,
  useApplyQuery
} from '@docyrus/ui/components/docyrus-query-builder';

export function QueryBuilderPlayground({ client, user, agentId }) {
  const query = useApplyQuery();
  const [aiOpen, setAiOpen] = useState(false);

  return (
    <DocyrusDataSourceQueryBuilder
      client={client}
      value={query.query}
      onChange={query.setQuery}
      aiAssistantOpen={aiOpen}
      onAiAssistantOpenChange={setAiOpen}
      renderAiAssistant={({ open, onClose, selectedDataSource }) => (
        <EditorAgent
          agentId={agentId}
          client={client}
          user={user}
          open={open}
          onClose={onClose}
          dataSourceId={selectedDataSource?.id ?? null}
          clientTools={query.tools}
        />
      )}
    />
  );
}

The matching backend agent must register a tool named applyQuery whose input schema mirrors { value: object (required), explanation?: string }.

Columns — current + parent data sources

The Columns section is a single grouped, multi-select tree (no separate "selected columns" pane). In both the current and parent groups, user fields (field-userSelect / field-userMultiSelect) and enum fields (field-select, field-radioGroup, field-enum, field-status, field-multiSelect, field-tagSelect) are expandable into a fixed sub-field catalog — user → id, firstname, lastname, email, photo; enum → id, autonumber_id, name, slug, icon, color. A checked sub-field nests at the correct depth, e.g. status(id, name) or company(owner(firstname, email)) (parent).

The header carries a Parent Data Sources switch (its state lives in the hook, so it survives switching to Run & Preview / JSON and back). It pulls fields from the data sources referenced by the current data source's field-relation columns and nests a checked field under its relation column, e.g. company(title, industry). It requires a client; fields come from GET /v1/dev/data-sources/:id/fields?expand=parent (the parent expansion returns each parent's full field list keyed by data source id). The same wiring is exposed on the hook as fetchRelatedFields(['parent', 'child']) for custom UIs.

Child Queries

The Child Queries panel (enabled via enableChildQueries) fetches related records as nested arrays, one per child data source. It uses the child expansion of the same endpoint (GET /v1/dev/data-sources/:id/fields?expand=child) to discover the data sources whose own field-relation points back to the current one:

  1. Pick a child data source from the Add child data source dropdown — this creates an entry whose From and Using are auto-filled and read-only (from = the child data source as a {appSlug}_{slug} reference, e.g. base_callcenter_call; using = its back-reference relation field slug).
  2. Select the columns to return via a multi-select tree-view of the child data source's own fields (with the same user / enum sub-field expansion as the Columns section).
  3. Optionally set Order By (a field dropdown + asc/desc direction) and Limit.

value.childQueries is an array of { alias, from, using, columns, orderBy?, limit? }. The alias (the result key the child rows are returned under) defaults to the child data source slug, and the editor automatically keeps it listed in the parent value.columns — the items endpoint drops the child block unless its alias is also a parent column (so the emitted request is ?columns=…,alias&childQueries=[{ alias, from, using, … }]). Removing an entry strips its alias from the columns again. Child data sources load when the panel opens (it flips relatedExpand.child); the fetch is de-duped per (dataSource, expand) signature.

Custom filter controls

The Filters section renders a QueryBuilderDocyrus. filterControlElements hands that builder a set of react-querybuilder control elements (merged over the Docyrus defaults) and filterContext travels alongside as react-querybuilder's context, so a host can replace the rule value editor without teaching this component anything about its domain — an automation editor offering Fixed value / Field / Expression modes that write #FIELD= / #FORMULA= markers, for example. Wrap QBValueEditor for the plain-value case so relation pickers, async options and between inputs keep working, and keep the object reference stable (module constant or useMemo) — a fresh object per render re-renders every rule.

import { QBValueEditor, type ValueEditorProps } from '@/components/docyrus/query-builder';

function RuntimeValueEditor(props: ValueEditorProps) {
  const { inputSchema } = props.context as { inputSchema: JsonSchema };

  return isMarker(props.value)
    ? <MarkerEditor {...props} schema={inputSchema} />
    : <QBValueEditor {...props} />;
}

const FILTER_CONTROLS = { valueEditor: RuntimeValueEditor };

<DocyrusDataSourceQueryBuilder
  value={query}
  onChange={setQuery}
  filterControlElements={FILTER_CONTROLS}
  filterContext={{ inputSchema }} />

Both props are no-ops when omitted.

API Reference

PropTypeDefaultDescription
valuePartial<ISelectQueryParams>requiredCurrent query payload
onChange(value: Partial<ISelectQueryParams>) => voidrequiredCalled whenever the query is edited
clientDocyrusQueryBuilderClient | nullnullAuthenticated Docyrus API client. Enables data-source discovery and query previews.
fieldsIField[][]Available fields for the selected data source. Overridden by the resolved data source when set.
dataSourcesIDataSourceReference[][]Pre-loaded data sources. Used as a fallback when no client is provided.
localeUiI18nLocale'en'UI locale token forwarded to translation-aware sub-components
variant'default' | 'bordered' | 'compact''default'Visual variant
size'sm' | 'default' | 'lg''default'Component size
classNamestring—Additional CSS class
defaultSectionDSQBSection'dataSource'Initially active configure section
lockDataSourcebooleanfalseLock the builder to the data source supplied via value (dataSourceId / dataSourceFullSlug). Hides the "Select Data Source" step and the clear button so the user can't switch data sources.
enableFormulasbooleantrueShow the formula editor section
enablePivotbooleantrueShow the pivot editor section
enableChildQueriesbooleantrueShow the child queries editor section
enableCalculationsbooleantrueShow the calculations editor section
aiAssistantOpenboolean—Controlled open state for the AI Assistant drawer. When provided the builder stops managing the open state internally — pair with onAiAssistantOpenChange.
onAiAssistantOpenChange(open: boolean) => void—Fired when the AI Assistant toolbar button toggles the drawer.
renderAiAssistant(ctx: IQueryBuilderAiAssistantRenderContext) => ReactNode—Mounts a custom AI Assistant drawer body. When set, the toolbar shows a Bot toggle that opens/closes the drawer; this render fn supplies the body (typically a <DocyrusAgent> or <EditorAgent> wrapper). The context exposes the live value payload and the currently-selectedDataSource so the slot can wire the agent's dataSourceId without re-deriving it.
aiAssistantWidthnumber380Width in pixels the drawer animates to when open.
filterControlElementsPartial<ControlElementsProp<FullField, string>>—react-querybuilder control elements for the filter-rule builders (today: the Filters section). Merged over the Docyrus defaults — set only the keys you replace, e.g. { valueEditor }. Pass a stable reference.
filterContextunknown—Forwarded as react-querybuilder's context to the filterControlElements.

Components

ComponentDescription
DocyrusDataSourceQueryBuilderTop-level component — renders the Configure / Run & Preview / JSON tabs
useApplyQueryHook that owns value state and exposes the applyQuery client-side tool for <EditorAgent> — the tool replaces the builder's query spec with the agent's draft. Refuses primitives / arrays / null so a bad LLM call cannot crash the builder.
DSQBDataSourceSelectorData source picker pane
DSQBColumnsEditorColumn selection + ordering pane
DSQBFiltersEditorFilter rule builder pane
DSQBOrderByEditorSort builder pane
DSQBPaginationEditorLimit + offset editor
DSQBCalculationsEditorAggregation / calculation rules editor
DSQBFormulaEditorFormula composer (block AST)
DSQBChildQueriesEditorChild queries editor
DSQBPivotEditorPivot matrix editor
DSQBSettingsEditorQuery mode + expand toggles
DSQBJsonPreviewRead-only JSON view of the current query
DSQBRunPreviewRun results table
DSQBFieldSelectorReusable field picker primitive

Type Exports

TypeDescription
DocyrusQueryBuilderPropsTop-level component props
DocyrusQueryBuilderClientMinimal HTTP client shape consumed by the component
DSQBContextValueShape of the internal context exposed by useDSQB() — the hook result plus the host slots filterControlElements / filterContext
DSQBPrimaryTab'configure' | 'preview' | 'json'
DSQBSectionConfigure section identifiers (data source, columns, filters, …)
DSQBPreviewStateRun-preview state (rows, columns, status, error)
ISelectQueryParamsFull query payload sent to the items endpoint
IQueryFilterGroup / IQueryFilterRule / QueryFilterTypeFilter tree primitives
ISelectQueryOrderBySort specifier
ISelectQueryCalculationRule / AggregateFunction / NumberTypeCalculation rule primitives
IQueryFormula / IQueryFormulaBlock / BlockKindFormula AST primitives
IQueryChildQueryParamsChild query specifier
ISelectPivot / ISelectPivotMatrixQuery / DateRangeIntervalPivot primitives
IDataSourceReferenceDiscovered or supplied data source descriptor
DSQBRelatedFields / DSQBParentFieldGroup / DSQBChildFieldGroupParent / child field groups returned by fetchRelatedFields()
DSQBRelatedExpand{ parent, child } expansion flags (relatedExpand / setRelatedExpand on the context)
IField / IFieldTypeDocyrus field descriptor + supported field-type tokens
QueryMode / ExpandTypeExecution flag tokens
IQueryValidationErrorValidation error shape returned by validateQuery()
ParsedColumnParsed column descriptor returned by parseColumnString()
UiI18nLocaleSupported locale tokens
IQueryBuilderAiAssistantRenderContextArgument passed to renderAiAssistant — { open, width, onClose, value, selectedDataSource }.
IUseApplyQueryResultReturn shape of useApplyQuery — { query, setQuery, tools }.

Helpers

ExportDescription
parseColumnString(columns)Parse a Docyrus columns string into ParsedColumn[]
serializeColumns(columns)Inverse of parseColumnString
normalizeOrderBy(orderBy)Coerce any orderBy value into an array
serializeOrderBy(orderBy)Serialize orderBy into the API string form
buildDefaultColumns(fields)Build a default columns string from a field list
cleanPayload(query)Strip null / empty values from a query payload before sending
getFieldTypeCategory(type)Map an IFieldType to its filter category
getOperatorsForFieldType(type)Get the operator list applicable to a field type
validateQuery(query)Run static validation, returning IQueryValidationError[]
countFilterRules(filters)Count leaves in a filter tree
createBlock(kind)Create a default formula block
updateBlockAt(root, path, next)Immutably update a formula block at a path
removeBlockAt(root, path)Immutably remove a formula block at a path
insertBlockAt(root, path, next)Immutably insert a formula block at a path
getBlockLabel(block)Render a human-readable label for a formula block

On this page