Adding a Query Mode

SkillDev tools

Use when adding a new analysis/query mode to drizzle-cube (e.g., cohort, path analysis). Covers backend types, query builder, executor/compiler integration, frontend adapter, store slice, hooks, UI components, tests, and icon.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Adding a Query Mode skill

What this skill tells your AI

The instructions your AI receives, as published by cliftonc/drizzle-cube in .claude/skills/add-query-mode/SKILL.md and read by ahel’s review.

Drizzle-cube has four analysis modes: query, funnel, flow, and retention. Each mode requires coordinated changes across backend (types → query builder → executor → compiler → cache), frontend (types → adapter → store slice → hooks → UI), tests, and an icon. Follow this checklist in order.

Documentation Context

Before designing or updating an analysis/query mode, review https://www.drizzle-cube.dev/llms.txt for the current documentation map, public API guidance, and mode-related examples. Use repository source and the checklist below as the implementation source of truth when docs and code differ.

Checklist

Backend

  • 1. Create type definitions — Create src/server/types/{mode}.ts with {Mode}QueryConfig, {Mode}ResultRow, and {Mode}ValidationResult interfaces. Export from src/server/types/index.ts. Add optional {mode}?: {Mode}QueryConfig field to SemanticQuery in src/server/types/query.ts.

  • 2. Create query builder — Create src/server/builders/{mode}-query-builder.ts with: has{Mode}(query) detection, validateConfig(config, cubes) validation, build{Mode}Query(config, cubes, context) SQL generation via Drizzle, and transformResult(rawResult) post-processing.

  • 3. Integrate with executor — In src/server/executor.ts, add a {mode}QueryBuilder field to the QueryExecutor class, initialize it in the constructor, add detection in the execute() method, add routing to a new execute{Mode}QueryWithCache() method, and implement execute{Mode}Query() and dryRun{Mode}() methods.

  • 4. Integrate with compiler — In src/server/compiler.ts, add a dryRun{Mode}() public method, add a validation path in validateQueryAgainstCubes(), and add routing in explainQuery().

  • 5. Update cache utils — In src/server/cache-utils.ts, add normalize{Mode}Config() and wire it into normalizeQuery().

  • 6. Database adapter updates (if mode needs DB-specific SQL) — Add abstract methods to src/server/adapters/base-adapter.ts and implement in postgres, mysql, sqlite, duckdb, snowflake, singlestore, and databend adapters.

  • 7. Update AI prompts (if AI should understand this mode) — Update src/server/prompts/types.ts and src/server/prompts/step1-shape-prompt.ts with mode-specific query structures.

Frontend

  • 8. Create frontend types — Create src/client/types/{mode}.ts with {Mode}SliceState, Server{Mode}Query, {Mode}ChartData, and type guards. Export public types from src/client/index.ts.

  • 9. Update AnalysisConfig types — In src/client/types/analysisConfig.ts, add '{mode}' to the AnalysisType union, create {Mode}AnalysisConfig extending AnalysisConfigBase, and add to the AnalysisConfig union type.

  • 10. Create mode adapter — Create src/client/adapters/{mode}ModeAdapter.ts implementing ModeAdapter<{Mode}SliceState> with: type, createInitial(), extractState() (critical for workspace persistence), canLoad(), load(), save(), validate(), clear(), getDefaultChartConfig(). Register via adapterRegistry.register() in src/client/adapters/adapterRegistry.ts.

  • 11. Create store slice — Create src/client/stores/slices/{mode}Slice.ts with state interface, actions (including build{Mode}Query() returning Server{Mode}Query | null and is{Mode}ModeEnabled()), and createInitial{Mode}State(). Export from src/client/stores/slices/index.ts. Compose into src/client/stores/analysisBuilderStore.tsx.

  • 12. Create query hook — Create src/client/hooks/queries/use{Mode}Query.ts handling debouncing, caching, and result transformation via useQuery. Export from src/client/hooks/queries/index.ts. A single shared useDryRunQuery.ts already handles SQL preview for all modes.

  • 13. Update query execution — In src/client/hooks/useAnalysisQueryExecution.ts, add server{Mode}Query to the options interface and add mode routing. In src/client/hooks/useAnalysisBuilderHook.ts, add mode-specific state and actions to the hook result.

  • 14. Create UI components — Create src/client/components/AnalysisBuilder/{Mode}ModeContent.tsx (main mode panel) and {Mode}ConfigPanel.tsx (configuration UI). Update AnalysisQueryPanel.tsx to conditionally render {Mode}ModeContent. Update AnalysisTypeSelector.tsx to add the mode option.

  • 15. Update portlet integration — Update src/client/components/AnalyticsPortlet.tsx and src/client/components/PortletContainer.tsx to detect and handle the new mode's queries.

  • 16. Add icon — In src/client/icons/customIcons.ts, create {mode}Icon: IconifyIcon with appropriate SVG path data.

  • 17. Chart type integration (if mode needs a new chart type) — Create chart component in src/client/components/charts/, update src/client/shared/chartDefaults.ts getChartAvailability(), and update ChartTypeSelector excludeTypes.

Testing

  • 18. Backend tests — Create tests/{mode}-query.test.ts covering query builder methods, execution integration, security context isolation, cross-cube filtering, and multi-database compatibility (TEST_DB_TYPE env).

  • 19. Client adapter tests — Create tests/client/adapters/{mode}ModeAdapter.test.ts covering createInitial(), canLoad(), load(), save(), validate(), clear(), getDefaultChartConfig(), and a round-trip test (save → load state equivalence).

  • 20. Client validation and execution tests — Create tests/client/{mode}/{mode}Validation.test.ts and tests/client/{mode}/{mode}Execution.test.ts for validation functions, data transformations, and metric calculations.

File Reference

FileActionKey Symbols
src/server/types/{mode}.tsCreate{Mode}QueryConfig, {Mode}ResultRow
src/server/types/index.tsExport new types
src/server/types/query.tsAdd field to unionSemanticQuery
src/server/builders/{mode}-query-builder.tsCreatehas{Mode}(), build{Mode}Query(), validateConfig(), transformResult()
src/server/executor.tsAdd builder + routingQueryExecutor, execute(), execute{Mode}QueryWithCache()
src/server/compiler.tsAdd public methodsdryRun{Mode}(), validateQueryAgainstCubes(), explainQuery()
src/server/cache-utils.tsAdd normalizernormalize{Mode}Config(), normalizeQuery()
src/client/types/{mode}.tsCreate{Mode}SliceState, Server{Mode}Query, {Mode}ChartData
src/client/types/analysisConfig.tsExtend union + configAnalysisType, {Mode}AnalysisConfig
src/client/adapters/{mode}ModeAdapter.tsCreate{mode}ModeAdapter : ModeAdapter
src/client/adapters/adapterRegistry.tsRegister adapteradapterRegistry.register()
src/client/stores/slices/{mode}Slice.tsCreatecreate{Mode}Slice, createInitial{Mode}State()
src/client/stores/analysisBuilderStore.tsxCompose slicecreateAnalysisBuilderStore()
src/client/hooks/queries/use{Mode}Query.tsCreateuse{Mode}Query()
src/client/hooks/useAnalysisQueryExecution.tsAdd mode routingserver{Mode}Query
src/client/hooks/useAnalysisBuilderHook.tsAdd state/actions
src/client/components/AnalysisBuilder/{Mode}ModeContent.tsxCreate
src/client/components/AnalysisBuilder/{Mode}ConfigPanel.tsxCreate
src/client/components/AnalysisBuilder/AnalysisTypeSelector.tsxAdd option
src/client/components/AnalysisBuilder/AnalysisQueryPanel.tsxAdd conditional render
src/client/components/AnalyticsPortlet.tsxAdd mode detection
src/client/icons/customIcons.tsAdd icon{mode}Icon : IconifyIcon

Reference Implementations

Study these existing modes as patterns (paths only — read the files directly):

Funnelsrc/server/builders/funnel-query-builder.ts, src/server/types/funnel.ts, src/client/adapters/funnelModeAdapter.ts, src/client/types/funnel.ts, src/client/stores/slices/funnelSlice.ts, src/client/hooks/queries/useFunnelQuery.ts, src/client/components/AnalysisBuilder/FunnelModeContent.tsx, src/client/components/AnalysisBuilder/FunnelConfigPanel.tsx, tests/funnel-query.test.ts, tests/client/adapters/funnelModeAdapter.test.ts

Flowsrc/server/builders/flow-query-builder.ts, src/server/types/flow.ts, src/client/adapters/flowModeAdapter.ts, src/client/types/flow.ts, src/client/stores/slices/flowSlice.ts, src/client/hooks/queries/useFlowQuery.ts, src/client/components/AnalysisBuilder/FlowModeContent.tsx, tests/flow-query.test.ts, tests/client/adapters/flowModeAdapter.test.ts

Retentionsrc/server/builders/retention-query-builder.ts, src/server/types/retention.ts, src/client/adapters/retentionModeAdapter.ts, src/client/types/retention.ts, src/client/stores/slices/retentionSlice.ts, src/client/hooks/queries/useRetentionQuery.ts, src/client/components/AnalysisBuilder/RetentionModeContent.tsx, src/client/components/AnalysisBuilder/RetentionConfigPanel.tsx, tests/retention-query.test.ts, tests/client/adapters/retentionModeAdapter.test.ts

Verification

  • npx tsc --noEmit — zero type errors
  • npm test -- {mode}-query.test.ts — backend tests pass
  • npm test -- {mode}ModeAdapter.test.ts — adapter tests pass
  • npm run build — successful build
  • New mode appears in AnalysisTypeSelector with correct icon
  • Mode adapter round-trips correctly (save → load preserves state)

Signals

GitHub stars
61
Forks
17
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
add-query-mode
Source
github.com/cliftonc/drizzle-cube