A powerful, modern FetchXML query builder and data explorer for Power Platform ToolBox. Inspired by the XrmToolBox FetchXML Builder, reimagined with React 18, Fluent UI v9, and seamless Dataverse integration.
- Tree-based hierarchy β Build FetchXML queries visually with entities, attributes, filters, orders, and link-entities
- Smart property editors β Context-aware panels for each node type (fetch options, conditions, relationships)
- Full FetchXML support β Aggregate queries, grouping, distinct, top/count, paging, and advanced hints
- Nested filters β Create complex AND/OR filter groups with unlimited nesting
- Link-entity relationships β Browse and add 1:N, N:1, and N:N relationships with inner/outer joins
- Virtualized DataGrid β Handle large result sets with smooth scrolling (powered by react-window)
- Rich cell rendering β Power Apps-style display for lookups, option sets, dates, currency, and more
- Multi-column sorting β Click headers to sort; Shift+click for multi-column sort
- Row selection β Select single or multiple records for bulk operations
- Resizable & reorderable columns β Customize your view with drag-and-drop columns
- Formatted values β Display OData formatted values or raw values (configurable)
- Monaco editor β Full-featured XML editor with XML syntax highlighting and line numbers
- Bi-directional editing β Edit XML directly and parse back to the visual builder
- Copy to clipboard β One-click copy of generated FetchXML, available in both read-only and editor modes
- Alias validation β Invalid alias characters are sanitized in real time in the property editors; the parser auto-corrects aliases on Parse to Tree with a clear description of each correction
- LayoutXML preview β See the column layout configuration
- Load system/personal views β Browse and load existing Dataverse views
- Optimized view execution β Uses SavedQuery/UserQuery APIs for better performance
- Save to Dataverse β Save your queries as new personal views or update existing ones
- Solution-aware β Add views to solutions during save
- Local Excel export β Native
.xlsxgeneration via ExcelJS with proper data types (numbers, dates, currencies, booleans) - Dataverse Excel export β Server-side export via the
ExportToExcelaction (requires a saved view) - Native save dialog β When running inside PPTB, both export paths use
fileSystem.saveFilefor a native OS save-file dialog; falls back to browser blob download in standalone/dev mode - Formatted value options β Export formatted values, raw values, or both side-by-side columns
- Record deletion β Delete selected records with confirmation dialog
- Bulk delete jobs β Submit async
BulkDeletejobs to Dataverse with job-tracking URL - Batch delete β Parallel per-record delete for small sets (up to 100 records) with progress and ETA
- Run workflows β Execute on-demand workflows on selected records with batch execution and progress tracking
- Select Attributes dialog β Multi-select attributes from a searchable DataGrid; accessible from entity and link-entity context menus
- Smart updates β Adds new selections and removes deselected attributes while preserving existing order and properties
- Search & filter β Real-time filtering across logical name, display name, and data type columns
- Code tab β New tab in the right pane that generates ready-to-use code from the current query in seven formats
- C# QueryExpression β Full SDK object graph (
QueryExpression,ColumnSet,FilterExpression,ConditionExpression,LinkEntity,OrderExpression) via the DataverseFetchXmlToQueryExpressionAPI; supports aggregate queries withXrmAggregateTypeandXrmDateTimeGrouping - C# FetchExpression β Simple
new FetchExpression(@"...")wrapper using a verbatim string literal - JavaScript β
Xrm.WebApi.retrieveMultipleRecordscall for model-driven apps and PCF components - pac CLI β
pac org fetch --xml "..."command for the Power Platform CLI - Power Automate β Field-by-field form mirroring the Dataverse "List rows" connector UI; table name resolved from
EntityDefinitionsmetadata (not a heuristic), each field individually copyable - Web API β Full OData URL with request headers, cURL snippet, and PowerShell
Invoke-RestMethodexample; entity set name resolved from metadata - SQL β Dataverse-generated T-SQL via
FetchXMLToSQL(undocumented API, labelled Preview)
- Dark/Light themes β Follows Power Platform ToolBox theme with Fluent UI tokens
- Lazy metadata loading β Loads only what's needed, when it's needed
- Intelligent caching β In-memory cache prevents duplicate API calls per session
- Resizable panes β Adjust split-pane layout to your preference
- Keyboard shortcuts β
Ctrl+Enterto execute query, copy XML to clipboard - Display settings β Toggle logical names vs display names in column headers; choose formatted, raw, or both value modes in the grid and exports
- Query scope settings β Control which entities appear in the entity picker: Publisher + Solution (full filter), Solution Only, or All Entities (no filter). Persisted across sessions.
- Advanced Find Only toggle β When on (default), limits entities and attributes to those marked
IsValidForAdvancedFind = true. Disable to access all entities including system and developer tables. Filter is applied locally β toggling is instant with no additional API call.
- Send to Tool button β Launch another PPTB tool directly from FetchXML Studio, pre-loading it with the current FetchXML query and active Dataverse connection (one-way handoff).
- Automatic tool discovery β Targets are discovered via the PPTB capability registry β installed tools that declare the
fetchxmlcapability. The Send to Tool control always renders as a dropdown listing the discovered tools (even a single match). No configuration needed. - Active connection forwarding β The active Dataverse connection is forwarded automatically so the target tool opens against the same environment.
- Inbound prefill β FetchXML Studio also accepts incoming T2T invocations from other tools (see Callee Contract below).
- Export privilege check β Only shows Dataverse export option if user has access
- Delete privilege check β Validates entity-specific delete permissions before enabling delete actions
- Bulk delete privilege check β Validates
prvBulkDeletebefore surfacing bulk delete - Workflow privilege check β Validates
prvReadWorkflow+prvWorkflowExecutionbefore showing workflow picker - View save privilege check β Validates
prvWriteQuery/prvWriteCustomization/prvPublishCustomization/prvWriteUserQuerybefore save operations
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β [Entity Selector βΌ] [Load View βΌ] [Save View] [Send to Tool βΌ] [β] β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β [FetchXML] [LayoutXML] [Results] [Code] [βΆ Execute]β
β Query Tree ββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β β
β π fetch β Results Grid / Monaco Editor β
β ββπ entity β β
β ββπ attribute β ββββββββββββββ¬βββββββββββββ¬βββββββββββββββ β
β ββπ attribute β β Column 1 β Column 2 β Column 3 β β
β ββπ½ order β ββββββββββββββΌβββββββββββββΌβββββββββββββββ€ β
β ββπ filter β β Value β Value β Value β β
β β ββ condition β β Value β Value β Value β β
β ββπ link-entityβ ββββββββββββββ΄βββββββββββββ΄βββββββββββββββ β
β β β
ββββββββββββββββββββββββ€ [β Prev] Page 1 of 10 [Next βΆ] 100 records β
β β β
β Properties Panel β [+ Add Columns] [β Edit Columns] [β¬ Export] β
β (context-aware) β [π Delete] [Bulk Delete] [β‘ Run Workflow] β
β β β
ββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββ
- Power Platform ToolBox desktop application
- Connection to a Dataverse environment
FetchXML Studio is available as a tool in Power Platform ToolBox. Install it from the tool gallery or load as a custom tool.
# Clone the repository
git clone https://github.com/mohsinonxrm/pptb-fetchxml-studio.git
cd pptb-fetchxml-studio
# Install dependencies
npm install
# Start development server
npm run dev# Production build
npm run build
# Preview production build
npm run preview
# Validate package manifest against PPTB registry rules
npm run validate
# Finalize for publishing (build + shrinkwrap)
npm run finalize-packageEnable debug logging in the browser console:
// Enable specific category
enableDebug("metadataAPI"); // API calls and responses
enableDebug("treeExpansion"); // Tree node operations
enableDebug("relationshipPicker"); // Relationship loading
enableDebug("linkEntityEditor"); // Link-entity configuration
enableDebug("propertiesPanel"); // Property editor routing
// Enable all
enableAllDebug();
// Disable
disableDebug("metadataAPI");
disableAllDebug();| Technology | Version | Purpose |
|---|---|---|
| React | 18.3 | UI framework |
| TypeScript | 5.9 | Type-safe development |
| Vite | 7 | Build tooling and HMR |
| Fluent UI v9 | 9.72 | Microsoft design system (Tree, DataGrid, Tabs, Drawer, etc.) |
| Monaco Editor | 0.54 | VS Code XML editor for FetchXML authoring |
| react-window | 2 | Virtualized list rendering for large datasets |
| ExcelJS | 4.4 | Native .xlsx generation with typed cells |
| @pptb/types | 1.2.3 | Power Platform ToolBox host API types (window.dataverseAPI, window.toolboxAPI) |
src/
βββ app/
β βββ AppShell.tsx # Main layout with resizable split panes
βββ features/fetchxml/
β βββ api/
β β βββ pptbClient.ts # window.dataverseAPI wrapper (all Dataverse ops)
β β βββ dataverseMetadata.ts # Lazy metadata loading with cache + dedup
β β βββ excelExport.ts # Local ExcelJS export with native types
β β βββ formattedValues.ts # OData @FormattedValue annotation helpers
β β βββ invocation.ts # T2T caller API (isT2TSupported, sendFetchXmlToTool)
β βββ model/
β β βββ nodes.ts # FetchXML node TypeScript definitions
β β βββ fetchxml.ts # FetchXML XML generation
β β βββ fetchxmlParser.ts # FetchXML XML β node tree parser
β β βββ fetchxmlIntellisense.ts # Intellisense / autocomplete helpers
β β βββ layoutxml.ts # LayoutXML generation
β β βββ operators.ts # Operator definitions by attribute type
β β βββ displaySettings.ts # Display settings types and defaults
β β βββ treeUtils.ts # Tree traversal utilities
β βββ engine/
β β βββ queryExpressionTypes.ts # QE JSON schema interfaces (number | string enums)
β β βββ queryExpressionCodegen.ts # C# SDK code emitter (resolveEnum, unwrapODataValue)
β β βββ fetchxmlCodeGenerators.ts # Sync generators: FetchExpr, JS, pac, PA, WebAPI
β βββ state/
β β βββ builderStore.tsx # React context + reducer state management
β β βββ cache.ts # Per-session in-memory metadata cache
β βββ ui/
β βββ LeftPane/ # Tree view + context-aware properties panel
β β βββ PropertiesPanel/
β β βββ editors/ # Node-specific property editors
β βββ RightPane/ # Monaco editor, LayoutXML viewer, results grid,
β β # code generation panel, Power Automate pane
β βββ Toolbar/ # Entity selector, load view picker, save button,
β β # send-to-tool button
β βββ Dialogs/ # Save view, select attributes, delete, bulk
β β # delete, workflow picker, solution picker
β βββ Settings/ # Settings drawer (display preferences)
βββ shared/
βββ components/ # Reusable value pickers (option set, boolean,
β # date, numeric, multi-value, relationship, etc.)
βββ contexts/ # ThemeContext
βββ hooks/ # usePptbContext, useLazyMetadata, useAccessMode,
β # usePublisherFilter, useSolutionFilter
βββ utils/ # Debug logging utilities
| Feature | Status |
|---|---|
| Basic queries | β |
| Attributes (select columns) | β |
| All-attributes | β |
| Filters (and/or) | β |
| Nested filters | β |
| All condition operators | β |
| Link-entity (joins) | β |
| Inner/outer joins | β |
| N:N relationships | β |
| Orders (sorting) | β |
| Multi-column sort | β |
| Aggregate queries | β |
| Groupby | β |
| Distinct | β |
| Top/Count | β |
| Paging with cookies | β |
| Value-of conditions | β |
| Entity name on conditions | β |
| Filter link-entity (any/all) | β |
| Query hints | β |
FetchXML Studio declares a PPTB Inter-Tool Invocation callee contract in pptb.config.json at the repository root, and declares the fetchxml capability so callers can discover it. Any other PPTB tool can launch FetchXML Studio and pre-populate it by passing a prefill payload that matches the following schema:
{
"fetchXml": "<fetch><entity name=\"account\">...</entity></fetch>"
}| Field | Type | Required | Description |
|---|---|---|---|
fetchXml |
string |
One of these three | Full serialized FetchXML query string. The root entity is parsed from this. |
viewRef |
object |
One of these three | EntityReference to an existing view. The FetchXML is retrieved from the view record. |
entityLogicalName |
string |
One of these three | Root entity logical name (e.g. "account"). Starts a fresh query on that entity. Ignored when fetchXml is present β the root entity is then derived from the FetchXML, since a mismatched value would break this metadata-driven tool's lookups. |
viewRef.id |
string (uuid) |
Required if viewRef | GUID of the view record |
viewRef.entityLogicalName |
"savedquery" | "userquery" |
Required if viewRef | Dataverse entity logical name β savedquery = system/public view, userquery = personal view |
Resolution priority at runtime: fetchXml β viewRef β entityLogicalName. With no prefill (a standalone launch) the tool opens empty.
When FetchXML Studio is launched by another tool, a Return FetchXML button appears in the FetchXML toolbar. Clicking it returns the current query to the caller:
{ "fetchXml": "<fetch>...</fetch>" }The caller receives this as the resolved value of launchTool(...). This is distinct from the host-injected "Return to [Caller]" banner, which simply navigates back and resolves the caller's promise with null (no data).
To launch FetchXML Studio from your own PPTB tool with a pre-loaded query:
const result = await window.toolboxAPI.invocation.launchTool(
"@mohsinonxrm/pptb-fetchxml-studio",
{ fetchXml: "<fetch><entity name=\"account\"><attribute name=\"name\"/></entity></fetch>" },
{ primaryConnectionId: connection?.id ?? null },
);
// If the user clicks "Return FetchXML", result is { fetchXml }.
// If they close the window or click the host's back banner, result is null.
const editedFetchXml = (result as { fetchXml?: string } | null)?.fetchXml;FetchXML Studio discovers send targets automatically β no configuration needed. It calls the host capability registry (toolboxAPI.invocation.findToolsByCapability("fetchxml")) to find installed tools that declare the fetchxml capability in their own pptb.config.json, and lists them in a Send to Tool button (a single button for one match, a dropdown picker for several). The button is hidden when no other fetchxml-capable tools are installed, or on hosts that don't support capability discovery.
The launch is one-way (noReturn: true) β FetchXML Studio hands the current query off and does not wait for data back. Any tool that wants to appear here only needs to declare:
{ "invocation": { "version": "1.0.0", "capabilities": ["fetchxml"], "prefill": { "properties": { "fetchXml": { "type": "string" } } } } }Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project follows Conventional Commits:
feat:New featurefix:Bug fixrefactor:Code refactoringdocs:Documentationchore:Maintenance
- Import/export query definitions (JSON)
- Quick query templates
- Query history
- Web API code generation
- Query performance insights
- Syntax validation in Monaco
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only). See LICENSE for details.
- XrmToolBox FetchXML Builder - Original inspiration
- Power Platform ToolBox - Host platform
- Fluent UI - UI component library
- Monaco Editor - Code editor
Built with β€οΈ for the Power Platform community