See your company like never before
Power BI deep-dives, migration playbooks, and data strategy for enterprise teams.


Join dozens of organizations who have moved to Beyond The Analytics. Book your personalized demo today
Take a Power BI semantic model to a deployed Fabric App with Claude Code: the rayfin commands, connector code, RLS tests and the errors that block deploys.
Quick answer: Scaffold Microsoft's data app template with the Rayfin CLI, add a semantic model connector, let Claude Code build the pages from the template's AGENTS.md and skills, validate the charts and the browser layout, deploy with npx rayfin up, then test row-level security as a real Viewer. Fabric Apps is still in preview.
This walkthrough starts from a published Power BI semantic model with its measures, format strings and row-level security in place. The goal is a custom web app on top of it, built by Claude Code, hosted in Fabric and signed in through Fabric SSO.
Microsoft documents this path for GitHub Copilot in Create a semantic model app with GitHub Copilot; its Claude Code tutorial builds a CSV explorer instead. The commands below come from those pages, the data app template documentation and the template repository, checked on 18 October 2026. Where the docs are silent or disagree with the template, the post says so.
The Fabric Apps overview is still titled "What is Fabric Apps (Preview)?" For what the product is, see the Fabric Apps explainer; for whether you need one, Fabric Apps vs a custom web app. If Claude is new to your Power BI work, start with the MCP setup walkthrough and the PBIR pipeline.
Quick answer: A workspace on Fabric capacity in a supported region, the Fabric Apps and Semantic Model Execute Queries REST API tenant settings switched on, Contributor or higher on the workspace, Build and Read on the semantic model, Node.js 20 or later, and a Claude plan that includes Claude Code.
| Requirement | What Microsoft Learn states | Owner |
|---|---|---|
| Capacity | "Your workspace must have Fabric capacity assigned." No minimum SKU is named. | Capacity admin |
| Fabric Apps setting | Under Fabric Apps (preview), toggle Enabled for the organisation or chosen security groups | Fabric admin |
| Model API setting | Semantic Model Execute Queries REST API, under Integration settings | Fabric admin |
| Region | Fabric App (preview) is unavailable in 18 regions on the region availability page | Capacity admin |
| Workspace role | Contributor, Member or Admin | Workspace admin |
| Semantic model | Build and Read, on a model hosted on Fabric or Power BI capacity | Model owner |
| Local tools | Node.js 20 or later; Claude Code on a Claude Pro, Max, Team, Enterprise or Console account | You |
Both tenant settings sit under OneLake catalog > Govern > Configurations > Tenant settings, or Admin portal > Tenant settings where that navigation has not rolled out. Microsoft says a change can take up to 15 minutes to reach everyone.
Gulf regions. UAE North has no Fabric features listed as unavailable, so Fabric Apps runs on a UAE North capacity. UAE Central and Qatar Central are Power BI-only regions. What counts is the region of the capacity assigned to the workspace.
Claude Code. On Windows, Microsoft's tutorial installs it with winget install Anthropic.ClaudeCode; on macOS, Linux and WSL, with curl -fsSL https://claude.ai/install.sh | bash. Check with claude --version and claude doctor. Other methods are on Anthropic's setup page.
IDs. Note the model's workspace ID and item ID. The template's AGENTS.md gives the URL they come from: https://app.fabric.microsoft.com/groups/<workspaceId>/datasets/<itemId>/.... Only models published to the service work; a local .pbix is not supported.
Quick answer: Run npm create @microsoft/rayfin@latest -- "<app-name>" --template dataapp --workspace <workspace-name>. The template ships Fabric sign-in, DAX guidance, visual components and a data grid, plus an AGENTS.md and agent skills that Claude Code can read.
From an empty parent folder:
npm create @microsoft/rayfin@latest -- "sales-insights" --template dataapp --workspace <workspace-name>
cd sales-insights
claudeMicrosoft states that "the Rayfin CLI is the supported way to create apps by using the data app template." Start Claude Code inside the project folder, not the parent; working in the wrong folder is one of the troubleshooting entries on Microsoft's Claude Code page.
The template registry in @microsoft/rayfin-cli 1.36.2 (published 16 October 2026) points dataapp at microsoft/fabric-apps-analytic-templates, tag v1, labelled "Latest stable release for v1 contracts." On 18 October that tag resolved to the 6 October release; the 14 October release, tagged v4.3.0, is newer. Both have an AGENTS.md and one folder per skill under .agents/skills/:
| Skill | v1 (6 Oct) | v4.3.0 (14 Oct) | Covers |
|---|---|---|---|
schema-discovery | Yes | Yes | Model discovery with DAX INFO.VIEW.* functions |
dax-authoring | Yes | Yes | DAX patterns, time intelligence, a test loop |
query-design | Yes | Yes | What DAX computes versus what TypeScript shapes |
visuals | Yes | Yes | VegaVisual and DataGrid components |
app-design | Yes | Yes | Layout, design tokens, loading and error states |
fabric-cli, fabric-sdk | Yes | No | The older @microsoft/fabric-app-data client |
semantic-model-data | No | Yes | The useSemanticModelQuery hook over the Rayfin connector |
The v1 release reads the model through @microsoft/fabric-app-data and a fabric.yaml file. The v4.3.0 release, like Microsoft Learn, uses the Rayfin semantic model connector. Check package.json to see which one you have.
Nothing in the template is specific to Claude Code: there is no CLAUDE.md and no .claude/ folder, and the README names GitHub Copilot CLI as its agent. Claude Code drives it by reading the same files.
AGENTS.md sets the rules: no mock or hardcoded data, no guessed result schemas, no full schema fetch up front, and npm run validate:visual on each query before component code.
Claude Code reads AGENTS.md as project instructions when there is no CLAUDE.md in the working directory or any folder above it, per its memory documentation. That needs Claude Code v2.1.277 or later. A CLAUDE.md at a monorepo root means AGENTS.md is ignored; the documented fix is a project CLAUDE.md containing @AGENTS.md.
Skills are different. Claude Code loads project skills from .claude/skills/<skill-name>/SKILL.md, per its skills documentation, which does not list .agents/skills/. The template's skills are only read when something points at them. AGENTS.md links schema-discovery and app-design; for the rest, the prompt must, which is why Microsoft's starter prompt opens with "Read the repository instructions and skills." Copying each folder into .claude/skills/ makes them load like native skills. Microsoft does not document that step, and the walkthrough works without it.
Quick answer: Add the model with npx rayfin connector add --type fabric-semanticmodel ... --operations executeQuery, run the version-matched npm install the CLI prints, and query it through client.connectors.<name>.executeQuery(). Don't call the Execute Queries REST API directly.
Find the model, then add it. The --name value becomes the property on the client:
npx rayfin connector search --workspace-id <workspace-id> --type fabric-semanticmodel --json
npx rayfin connector add --type fabric-semanticmodel --workspace-id <workspace-id> --item-id <semantic-model-item-id> --name salesModel --operations executeQueryThe second command writes the connector into rayfin/rayfin.yml, generates a schema under rayfin/connectors/salesModel/, and prints an npm install command pinned to your CLI version. Run "the exact install command printed by the CLI." The config it writes:
connectors:
- name: salesModel
type: fabric-semanticmodel
config:
workspaceId: "<workspace-id>"
itemId: "<semantic-model-item-id>"
auth:
type: delegated
version: "1"
operations:
- name: executeQueryKeep the generated version. type: delegated means every query runs as the signed-in user, which is what lets the model's RLS apply.
Microsoft's client wiring and query:
import { ConnectorsRayfinClient } from '@microsoft/rayfin-client';
import { fabricSemanticModel } from '@microsoft/rayfin-connector-fabric-semanticmodel';
import {
connectorConfig,
type SalesModelSchema,
} from '../../rayfin/connectors/salesModel/schema.js';
type AppConnectorsSchema = {
salesModel: SalesModelSchema;
};
export const client = new ConnectorsRayfinClient<
Record<string, never>,
Record<string, never>,
AppConnectorsSchema
>(
{
baseUrl: '<app-api-url>',
publishableKey: '<publishable-key>',
authStorage: true,
connectors: { salesModel: connectorConfig },
},
{ salesModel: fabricSemanticModel() }
);
const result = await client.connectors.salesModel.executeQuery({
query: 'EVALUATE TOPN(10, Sales)',
});
if (result.status === 'success') {
console.log(result.table.columns, result.table.rows);
} else {
console.error(result.error.category, result.error.message);
}How much of this you write depends on the release:
v4.3.0. Already wired. src/lib/connectors.ts discovers each rayfin/connectors/*/schema.ts at build time, src/lib/rayfin-client.ts builds the client from VITE_RAYFIN_API_URL and VITE_RAYFIN_PUBLISHABLE_KEY, and components call the useSemanticModelQuery hook, which wraps executeQuery with error handling and a session cache. AGENTS.md tells the agent to ignore the "has manual edits" warning connector add prints.v1. Microsoft's instruction covers it: "If the scaffold includes another semantic model client, replace calls to it instead of maintaining two data-access paths." Have Claude Code replace @microsoft/fabric-app-data and remove it once nothing calls it.Either way, check the status before rows reach a visual. "Don't replace a failed connector query with mock data," says the template page. The error categories are api, query, network, overflow and unknown, and AGENTS.md asks for all five to be handled.
To test DAX before any UI exists, the template's skills put the query in a JSON file and run it through the connector:
npx rayfin connector invoke --name salesModel --operation executeQuery --file ./inspection/schema.query.jsonHere schema.query.json holds { "query": "EVALUATE INFO.VIEW.TABLES()" }. The dax-authoring skill forbids passing DAX inline with --input. The CLI reference also lists npx rayfin connector inspect --query <path>, which runs a .dax or .sql file read-only.
Quick answer: Describe the audience, questions, interactions and visuals. Tell Claude Code to read the repository instructions and skills, query model metadata before writing DAX, use only the connector client and keep the existing sign-in flow. Ask for a plan before any edit.
Microsoft's starter prompt is agent-neutral. Adapted to a sales model:
Build a sales performance explorer for regional sales managers, using the
existing salesModel connector.
Before editing:
1. Read the repository instructions and skills.
2. Inspect rayfin/connectors/salesModel/schema.ts and the template's data-access,
visualization, data grid, formatting, and validation patterns.
3. Query the semantic model metadata before writing DAX. Don't guess table,
measure, or column names.
Requirements:
- KPI cards for revenue, order count and margin, if those measures exist.
- A monthly revenue trend, a bar chart by region, and a data grid.
- Use ConnectorsRayfinClient and client.connectors.salesModel.executeQuery().
- Keep the existing Fabric sign-in flow. Don't add credentials, access tokens,
another authentication flow, or direct calls to the Execute Queries REST API.
- Reuse query results where practical, and bound large result sets.
- Apply semantic model format strings consistently.
- Include loading, empty, and error states.
Give me a short implementation plan, make the changes, run the existing build,
and report the results.Three habits keep the output reviewable:
[Total Sales] and the plan says [Revenue], correct it before approving edits.rayfin/rayfin.yml, new dependencies, and any fetch that bypasses the connector.Each executeQuery is a DAX query against your capacity, so shared queries cost less than one per visual. Fabric Apps pricing needs its own treatment; for polling limits, see real-time data in Fabric Apps.
Quick answer: Microsoft Learn describes a Playwright browser-validation workflow in the template, but the template releases the CLI points at in October 2026 ship only npm run validate:visual, a headless chart check. Run it, add Playwright MCP to Claude Code for the browser pass, and test through the Fabric portal.
What Learn says. The template page says to "use the included Playwright browser-validation workflow" to check that visuals render, charts aren't cut off, text is readable, grids handle overflow, loading, empty and error states work, filters and cross-highlighting behave, and the console is clean.
What the template ships. Neither the v1 nor the v4.3.0 release contains a Playwright dependency, config or skill. Validation is npm run validate:visual -- <query-factory.ts>, which renders each chart to SVG from its query factory, without a browser, and fails on error diagnostics or zero rendered data marks. That catches a query that returns nothing or a spec that draws nothing. It does not catch a clipped chart or an unreadable label.
A Playwright workflow does exist in the Rayfin repository's bundled samples/data-app (an app-validation skill plus playwright-cli), which the CLI uses only if the external template fails to load. Its rule is worth keeping: the app "can only be tested and validated with the Fabric portal embed flow," never against localhost.
What to do in Claude Code. Add Playwright's MCP server with the command from Playwright's Claude Code page:
claude mcp add playwright npx @playwright/mcp@latestThen preview as the template README describes: run npm run dev, open the app item in the Fabric portal, and append &devUri=http://localhost:5173 to its URL. Point the browser checks at that URL, sign in yourself, and don't let the agent enter credentials. Ask for desktop and mobile checks for clipped charts, unreadable labels, empty visuals and console errors.
This runs as your account, so it says nothing about RLS.
Quick answer: Run npx rayfin up to create or update the app item, apply settings and schema and upload the built frontend, then npx rayfin up status to confirm health. You need Edit permission on the item. A first deployment typically takes 2 to 5 minutes.
Have Claude Code show the diff, then run:
npx rayfin up
npx rayfin up statusPer the deploy page, npx rayfin up creates or reuses the item, retrieves the publishable key, syncs settings, applies the schema, builds and uploads static content, and writes deployment details to rayfin.yml and .env.fabric-* files. npx rayfin up --dry-run shows the plan first.
npx rayfin up again.rayfin/.env or rayfin/.temp/.Quick answer: The connector uses delegated authentication, so DAX runs as the signed-in user. Power BI applies RLS only to users with Viewer permission; workspace Admins, Members and Contributors bypass it. The developer account always sees every row, so test with a real Viewer.
The connectors page says delegated authentication makes "connector requests use the signed-in user's access." No Fabric Apps page shows a worked RLS example, so treat it as inherited Power BI behaviour and test it.
The rule that catches teams: "RLS only restricts data access for users with Viewer permissions. It doesn't apply to workspace Admin, Member, or Contributor roles," per the RLS documentation. The builder needs Contributor or higher, so the build account and Claude Code's validation both see every row, and a filter that never fires looks like one that works.
A test that proves it:
Expect one more failure mode. When RLS is defined, users with only Read or Build "can't read data from the semantic model unless they belong to one of its RLS roles," per the semantic model permissions page. An unmapped user gets empty results, not an error. Make the empty state say so.
Don't ask Claude Code to "fix" a total that RLS has filtered. External users are a separate topic; Fabric SSO through Entra ID is the only sign-in a deployed app supports.
Quick answer: Not through the semantic model connector, which supports only executeQuery. Write-back goes through the warehouse (fabric-warehouse) or SQL database in Fabric (fabric-sqldatabase) connectors, which list read, create, update and delete, or through the app's own SQL database.
The connectors page lists four types:
| Fabric item | Connector type | Operations |
|---|---|---|
| Lakehouse SQL analytics endpoint | fabric-sqlanalytics | read |
| Warehouse | fabric-warehouse | read, create, update, delete |
| SQL database in Fabric | fabric-sqldatabase | read, create, update, delete |
| Semantic model | fabric-semanticmodel | executeQuery |
The usual pattern: read numbers from the model, write user input (a forecast override, a comment, an approval) to a warehouse or SQL database table, and let the model pick it up. Learn's own example adds a SQL database connector with read only:
npx rayfin connector add --type fabric-sqldatabase --workspace-id <workspace-id> --item-id <sql-database-item-id> --name operational --operations readLearn never shows the write operations on a command line, so this post doesn't either; check the connector add options in the CLI reference. The write call shape is in Rayfin's connector docs in microsoft/rayfin (packages/typescript-sdk/connector-fabric-graphql/assets/docs/mutations.md):
const created = await client.connectors.orders.Order.create({
quantity: 3, unitPrice: 19.99,
});A write exists only when both the connector's operations: and the entity's @role(...) allow it. SQL database writes return the full row; warehouse writes return a DbOperationResult status and throw on failure.
Two traps are specific to the template. In v4.3.0, src/lib/connectors.ts supports only fabric-semanticmodel and throws at load time for any other type, naming the fix: import that connector's runtime and widen AppConnectorsSchema. And Microsoft warns "Don't infer a primary key from a column name," and that TypeScript types "aren't an authorization boundary"; row policies and @role declarations are.
Quick answer: The common failures are a blocked SSO popup, an SSO origin mismatch, schema drift between code and database, @text() fields with no max length breaking the data API, the 100 MB static deploy cap, and expired CLI sessions returning 401 or 403. Each has a documented fix.
From Microsoft's troubleshooting page:
| Symptom | Cause | Fix |
|---|---|---|
| Browser blocks the Fabric sign-in window | ensureSignedInWithFabric() called outside a user gesture | Call it from a synchronous click handler |
| SSO handoff rejected for origin mismatch | returnOrigin, allowedRedirectUris or fabricPortalUrl doesn't match | Bare origin in returnOrigin, listed in services.auth.allowedRedirectUris, then npx rayfin up |
| Deploy succeeds, data API returns an internal server error | @text() without max becomes NVARCHAR(MAX) and can block GraphQL schema generation | @text({ max: 200 }), then reapply the schema |
| Database schema apply fails | Remote schema and app code out of sync | Revert manual or unsupported agent changes, run npx rayfin up db apply |
| Static deploy fails with a size limit error | Compressed archive over 100 MB | Exclude source maps, shrink or move media |
401 or 403 on npx rayfin up | Expired CLI session | npx rayfin login, then retry |
SSO popup. Microsoft's fix:
async function handleClick() {
await ensureSignedInWithFabric(client.auth, options);
}
// Attach to button click
<button onClick={handleClick}>Sign in</button>Asked to sign users in automatically, an agent can move this call into a page-load effect, which is what the browser blocks. Tell Claude Code to keep the template's sign-in flow.
Origin mismatch. A bare origin is scheme, host and port with no path, such as http://localhost:5173. The redirect URI page calls allowedRedirectUris a security boundary: no wildcards and no stale entries copied from another project.
Schema drift. "The app code is the source of truth for a Fabric app." Adding a column is supported; renaming, retyping or removing one is not. Microsoft's fix list includes "instruct the agent to revert that change," so state the rule in your prompt up front. A renamed column or measure in the semantic model surfaces instead as an executeQuery error.
Text fields. The documented reapply after setting max is npx rayfin up db apply --force, and Microsoft warns that --force "can cause permanent data loss." When Claude Code proposes it, have it list the destructive operations first.
No. As of October 2026 the Microsoft Learn overview is titled "What is Fabric Apps (Preview)?" and the region page lists "Fabric App (preview)." Plan for preview terms: no ALM, no private links, no workspace outbound access protection, and behaviour that can change between CLI versions.
It works. Microsoft's semantic model tutorial targets GitHub Copilot, but the CLI commands are the same in any agent. Claude Code reads the template's AGENTS.md when no CLAUDE.md sits above it. It does not register the .agents/skills/ folders as skills, so tell it to read them or copy them into .claude/skills/.
The connector uses delegated authentication, so queries run as the signed-in user and the model's roles apply. Power BI enforces RLS only for users with Viewer permission; Admins, Members and Contributors see all rows. Microsoft has not published a Fabric Apps RLS example, so test with a real Viewer before sharing.
Yes, on UAE North, which has no Fabric features listed as unavailable on Microsoft's region page. UAE Central and Qatar Central are Power BI-only regions, and no Saudi Arabia region appears on the page as of October 2026. Fabric App is unavailable in 18 regions, including UK South and Canada Central.
Not through the semantic model connector, which only supports executeQuery. The warehouse and SQL database in Fabric connectors support read, create, update and delete. A write needs both the connector's operations and the entity's @role to allow it, and the data app template must be changed to accept a second connector type.
Microsoft Learn says it includes a Playwright browser-validation workflow. The template releases the CLI points at in October 2026 contain no Playwright files and ship npm run validate:visual, a headless chart check. In Claude Code, add Playwright MCP and test through the Fabric portal with devUri.
Microsoft Partner · Dubai
Your business intelligence partner for the GCC
Have a data challenge or a project in mind? Reach out and let's explore how we can help.
Follow us on Google
Make us a preferred source and our analytics writing gets priority in your Top Stories and AI answers.
Add to Preferred SourcesClients we've worked with







Topics