Microsoft Fabric Cloud Data Platforms Data Engineering

Microsoft Fabric Apps: TypeScript Developer Guide

Microsoft Fabric Apps: TypeScript Developer Guide
Microsoft Fabric Data Engineering Cloud Data Platforms

Microsoft Fabric Apps: Complete TypeScript Developer and Architect Guide

📅
⏱️22 min read
Microsoft Fabric · Data Engineering
Microsoft Fabric Apps architecture diagram showing four generated layers: static hosting, GraphQL API, Entra ID authentication, and managed SQL database on OneLake

Every Fabric app is four generated services — hosting, GraphQL API, auth, and SQL database — deployed from one TypeScript codebase.

Microsoft Fabric Apps is the new item type Microsoft introduced in public preview at Build 2026, and it fundamentally changes what building on Fabric means for a developer. Instead of a report, a pipeline, or a notebook, Microsoft Fabric Apps lets you define an entire application backend - schema, API, auth, hosting as TypeScript and deploy all of it with a single CLI command. This is the developer- and architect-level reference: every entity decorator, every CLI command, the full security model, and the edges you'll actually hit in preview.

Nothing here is written for an executive audience. If you're a Fabric developer, a data engineer extending a Lakehouse with a write path, a Power BI professional building an operational companion app, or a solution architect deciding whether Microsoft Fabric Apps belongs in a reference architecture, this is written for you directly with runnable code, not slideware.

📣 Feature Status

Microsoft Fabric Apps entered public preview at Microsoft Build 2026 (June 2, 2026). It carries supplemental preview terms, best-effort support, and no SLA. Do not commit production-critical workloads until General Availability.

Why Microsoft Introduced Fabric Apps

Fabric unified data engineering, warehousing, and BI on top of OneLake, but the one layer that stayed outside that story was the application layer the CRUD tool, the internal dashboard with write-back, the persistent-state backend an AI agent needs. Teams building that layer routinely reached outside Fabric entirely: a Postgres instance on Supabase or Neon, a hand-rolled Express API, a separate auth provider. That works, but it creates a second, ungoverned copy of enterprise data sitting outside sensitivity labels, workspace RBAC, and capacity billing exactly the kind of shadow IT a Fabric governance program exists to prevent.

Two forces pushed Microsoft to close that gap now. First, AI coding agents have made frontend generation nearly free, a working UI in under an hour is routine while the backend behind it (auth, schema, access policy, compliance wiring) is still a multi-week effort bolted on too late, if at all. Second, whatever that application writes eventually needs to land back in the governed estate, and a separate datastore means a separate ETL pipeline maintained indefinitely. Fabric Apps, built on the open-source Rayfin SDK, is Microsoft's answer: a TypeScript-first, agent-friendly way to define a backend that writes directly into OneLake, so the data is immediately usable by every other Fabric workload without a hand-off.

"The fastest way to lose control of your data estate is to let AI-generated application backends live outside it. Fabric Apps exists to close exactly that gap."

What Microsoft Fabric Apps Are: Key Features and Capabilities

A Fabric app is a managed backend-as-a-service item that lives in a Fabric workspace. You author it locally as a TypeScript project, and a single deploy provisions four generated services inside your tenant:

Data models to APIs
Decorate TypeScript classes with @entity(), @text(), @uuid(), and related decorators. Microsoft Fabric Apps generates the SQL schema and a GraphQL API from them automatically.
Type-safe client
The generated client SDK validates every query and mutation against your schema at compile time, catching mistakes before they reach the backend.
Built-in authentication
Session management, token handling, and Microsoft Entra ID SSO are wired in by default. Local development falls back to email/password.
Local development parity
The full stack runs locally via Docker, so iteration doesn't require a live Fabric deployment for every change.
OneLake-native storage
Application data and static frontend assets both live in OneLake, inheriting the same governance surface as the rest of your estate.

Two prerequisites gate all of this: the workspace needs a Fabric capacity assigned (Fabric Apps consumes capacity units from that pool, same as any other workload), and a tenant administrator must explicitly enable the Fabric Apps workload under Tenant settings before anyone in the tenant can create one optionally scoped to specific security groups rather than the whole org.

Problems Fabric Apps Solve

Four recurring pain points map directly onto Fabric Apps' design choices. Datastore proliferation, every internal tool spinning up its own database is solved by giving every app a Fabric-managed SQL database by default, no separate provisioning step. Reinvented authentication - every team writing its own login flow, token refresh, and session logic is solved by making Entra ID SSO a built-in, non-optional default in production. Governance blind spots application data nobody on the platform team can see or audit is solved by writing everything through OneLake, inheriting workspace RBAC and sensitivity labels automatically. And slow time-to-production for AI-agent backends - agents need persistent, structured state, not just stateless function calls is solved by giving an agent a governed CRUD API it can call the moment the schema is deployed.

Core Architecture

Two layers make up the system. The Rayfin CLI and SDK is the local toolchain: it scaffolds projects, runs a Docker-based local stack for development, and talks to Fabric only when you deploy. The Fabric app runtime is the cloud execution environment inside your tenant the managed service that appears in your workspace after rayfin up. Every deployed app exposes one endpoint that fronts all backend services:

https://<your-app>-app.rayfin.windows.net/
  ├── /api/graphql   → Data API used by the generated client
  ├── /auth          → Authentication service (Entra ID / Fabric SSO)
  └── /storage       → Static asset + file storage (OneLake-backed)

Each child service - SQL database, authentication, static content is provisioned as its own item under the Fabric app in the portal. That matters architecturally: each one is independently subject to workspace RBAC, sensitivity labels, and the platform's existing governance controls, rather than being an opaque black box bundled inside a single deployment artifact.

Microsoft Fabric Apps: TypeScript Development and Data Models

Every part of a Fabric app data models, backend logic, and client code is written in TypeScript. There's no separate schema language, no YAML DSL for entities, and no SQL migration files to hand-author. A data model is a plain class:

import { entity, uuid, text, int, decimal, boolean, date, set }
  from '@microsoft/rayfin-core';

@entity()
export class Product {
  @uuid() id!: string;
  @text({ min: 1, max: 120 }) name!: string;
  @decimal() price!: number;
  @int({ min: 0 }) stockQuantity!: number;
  @boolean({ default: true }) isAvailable!: boolean;
  @date() createdAt!: Date;
  @set('draft', 'published', 'archived') status!: 'draft' | 'published' | 'archived';
}

Fabric Apps reads the decorators at build time and generates the SQL DDL, the GraphQL schema, and a typed client proxy for Product three artifacts from one source of truth. Every entity gets an automatic UUID-typed id primary key unless you declare your own; composite primary keys and custom key column names aren't supported in preview.

Relationships are declared with @one() on the child side and @many() on the parent side:

@entity()
export class Category {
  @uuid() id!: string;
  @text() name!: string;
  @many(() => Product, 'category') products!: Product[];
}

@entity()
export class Product {
  @uuid() id!: string;
  @text() name!: string;
  @one(() => Category) category!: Category;
}

Fabric Apps generates the foreign-key column (category_id) automatically following a {property}_id convention. Many-to-many relationships aren't modeled directly, you build an explicit join entity with two @one() references. Every new entity must also be registered in rayfin/data/schema.ts so the client generator can produce typed proxies against it; forgetting this step is one of the most common early mistakes and produces a client method that silently doesn't exist.

The Decorator Reference

Decorators are the mechanism that turns a plain class into a fully generated backend. The core field decorators are @uuid(), @text() (with optional min/max length constraints), @int() and @decimal() for numeric fields, @boolean() with an optional default, @date() for timestamps, and @set(...) for enum-style constrained string values. Authorization is expressed with @role(), applied at the entity level:

@entity()
@role('authenticated', '*', {
  policy: (claims, item) => claims.sub.eq(item.user_id),
})
export class Task {
  @uuid() id!: string;
  @text() title!: string;
  @text() user_id!: string;
}

Here, @role('authenticated', '*', { policy }) means: any authenticated caller can attempt any operation (*) on Task, but the policy function narrows every read and write down to rows where the row's user_id matches the caller's subject claim. That single decorator replaces what would otherwise be a hand-written authorization middleware layer sitting in front of every route.

GraphQL APIs and the Type-Safe Client

Fabric Apps generates a full GraphQL schema from your entities, but in normal day-to-day development you don't write GraphQL queries by hand, you use the generated RayfinClient, a fluent, chainable API that builds the query underneath and gives you compile-time checking on every field, filter, and relationship you touch:

import { RayfinClient } from '@microsoft/rayfin-client';

const client = new RayfinClient<AppSchema>({
  baseUrl: import.meta.env.VITE_RAYFIN_API_URL,
  publishableKey: 'pk-your-project-key',
});

const active = await client.data.Product
  .select(['id', 'name', 'price', 'category.name'])
  .where({ status: { eq: 'published' }, stockQuantity: { gt: 0 } })
  .orderBy({ price: 'desc' })
  .execute();

Filters support the standard comparison set — eq, ne, gt, gte, lt, lte, contains — and dotted paths like category.name pull related-entity fields into the same request instead of triggering a second round trip. Large result sets should always go through cursor-based pagination:

const page = await client.data.Product
  .select(['id', 'name', 'price'])
  .orderBy({ createdAt: 'desc' })
  .first(25)
  .executePaginated();
// page.items, page.pageInfo.endCursor, page.pageInfo.hasNextPage

Two preview gaps worth designing around: there's no count() method on the client, and totalCount isn't populated on paginated results a UI that needs a running total currently needs its own workaround, such as a maintained counter entity or a client-side estimate.

CRUD Operations in Depth

Create, update, and delete follow the same fluent pattern as reads. A single-record create:

const created = await client.data.Product.create({
  name: 'Fabric Capacity Add-on',
  price: 499.0,
  stockQuantity: 25,
  isAvailable: true,
  createdAt: new Date(),
  status: 'draft',
});

Updates take a key selector and a partial payload, so you only send the fields that changed:

await client.data.Product.update({ id: created.id }, { status: 'published' });

Deletes take the same key selector:

await client.data.Product.delete({ id: created.id });

Creating a record with a relationship set on it means passing the related entity's ID, not the full object:

const item = await client.data.Product.create({
  name: 'Fabric Capacity Add-on',
  price: 499.0,
  stockQuantity: 25,
  isAvailable: true,
  createdAt: new Date(),
  status: 'draft',
  category: { id: existingCategoryId },
});

Because there's no built-in support for multi-step transactions or stored procedures in preview, any operation that needs several writes to succeed or fail together has to be handled at the application layer with explicit compensation logic not assumed to be transactional. This is one of the more consequential architectural constraints to flag early.

The Managed SQL Database

Every Fabric app provisions its own SQL database automatically, with schema generated entirely from your TypeScript entities. You can open it directly in the Fabric portal's object explorer and run read queries against it, genuinely useful for debugging a production issue without adding logging but the portal treats it as read-only for schema purposes by design. Schema changes must originate in code and flow through the CLI's schema-apply step; editing the schema directly in the SQL database item can produce a drift between what's deployed and what your code expects, breaking the app on the next deploy.

The architectural payoff is downstream: because the database is backed by OneLake, everything a Fabric app writes is immediately visible to the rest of the Fabric estate - Lakehouses, Warehouse SQL endpoints, notebooks, and Power BI semantic models with no separate ETL job to build.

Authentication with Microsoft Entra ID

In production, Microsoft Entra ID via Fabric SSO is the only supported authentication path there's no bring-your-own-identity-provider option for a deployed Fabric app. Users authenticate through their existing Fabric identity; there's no separate signup flow or credential store to build or secure. During local development, an email-and-password fallback lets you test the full application without a live Fabric deployment, which is a genuine convenience for rapid iteration, but it's strictly a local-dev mechanism. Authenticated users are visible directly in the SQL database, which gives you a straightforward audit trail of who has actually used an internal tool without building separate logging.

Security Model, Permissions, and Authorization

Fabric Apps layers three distinct controls. Row-level authorization is declared in code via @role() policies, evaluated per-request against the caller's claims, this is the only layer that can express "users can only see their own rows" logic. Item-level permissions sit on top, at the Fabric workspace level, in three tiers: Run and interact (open and use the app), Edit (deploy code, change schema, manage the child services), and Reshare (grant access to others). Workspace roles don't automatically cascade into item permissions sharing an app with a colleague still means granting the item-level permission explicitly.

Security responsibilityProvided by FabricOwned by your team
Sign-in / SSO✓ Entra ID, PKCE, HTTPS—
Row-level access rules✓ enforced via @role decorators✓ you write the policy logic
Workspace / item permissions✓ RBAC tiers built in✓ you grant them correctly
Secrets in frontend code—✓ static content is public by default
Transactional integrity across writes— no built-in multi-step transactions✓ compensation logic in app code

Hosting, Deployment, and the Application Lifecycle

Static frontend assets, HTML, CSS, JS - are hosted directly on OneLake-backed storage; there's no separate CDN or hosting account to configure. Deployment is a single CLI command, npx rayfin up, which applies schema changes, deploys the GraphQL API and auth configuration, and pushes the built frontend, all in one step. Schema-only changes can be applied independently with npx rayfin up db apply; because a destructive change can lose data, that path requires an explicit --force flag.

Multi-environment promotion is handled through the --workspace flag rather than a dedicated release-pipeline concept: npx rayfin switch --workspace <name> repoints your local project at a different target workspace (dev, test, production), and each subsequent rayfin up deploys there. There's no built-in staged-rollout or blue-green mechanism yet, treat this as a preview gap to design around rather than a solved problem.

Local Development Workflow

The Rayfin CLI and a local Docker stack are the backbone of iteration speed. A new project is scaffolded with:

npm create @microsoft/rayfin@latest -- "my-app" --template todoapp --workspace <workspace>

That pulls a starter template already wired to a target Fabric workspace. From the generated project directory, npm run dev brings up the full stack locally against Docker: schema, GraphQL API, and auth all running on your machine, with no dependency on a live Fabric deployment for the inner development loop. Microsoft explicitly positions GitHub Copilot as the intended pairing tool for writing entity and business-logic code inside this loop.

Creating Your First Microsoft Fabric App: Full Walkthrough

With the Fabric Apps workload enabled tenant-wide and a Fabric-capacity-backed workspace available, the path from zero to a deployed app is:

Step 01
Create the app item in your workspace
In your Fabric workspace, choose New item → App and give it a name. The portal generates a scaffold command tied to that workspace.
Step 02
Run the scaffold command locally
Copy the scaffold command from the portal and run it in your terminal: npm create @microsoft/rayfin@latest -- "my-app" --template todoapp --workspace <workspace>
Step 03
Navigate into the generated project
cd into the generated project folder. The scaffold produces a React + TypeScript frontend already wired to the Rayfin SDK, plus a rayfin/ folder holding your backend definition.
Step 04
Define your entities and register them
Define or edit entities under rayfin/data/, then register each new entity in rayfin/data/schema.ts so the client generator produces typed proxies for it. Forgetting this step causes silent failures.
Step 05
Run locally against the Docker stack
Run npm run dev to bring up the full stack locally via Docker — schema, GraphQL API, and auth all on your machine. No live Fabric deployment needed for the inner development loop.
Step 06
Deploy to Microsoft Fabric
Run npx rayfin up to deploy schema, API, auth configuration, and frontend together to Fabric in a single command. This provisions all four child services inside your workspace.
Step 07
Confirm the deployment in the Fabric portal
Open the app item in the Fabric portal and confirm the App URL, App Backend URL, and the generated SQL database are all present. Your Microsoft Fabric App is live.

Integration with Other Fabric Services

The OneLake-native design is what makes Fabric Apps interoperate with the rest of the platform without custom plumbing. Data an app writes is queryable from a Lakehouse or Warehouse SQL endpoint immediately, usable inside a notebook for downstream transformation or model training, and available to a Power BI semantic model as a DirectLake or import source - all without a scheduled extract. For Real-Time Intelligence scenarios, an app's write path can feed an Eventstream if the use case calls for near-real-time downstream processing. This is the practical reason a solution architect would choose Fabric Apps over an external Postgres-backed app for anything whose output needs to show up in existing BI or data engineering workflows.

Business Use Cases

Microsoft frames four patterns as the primary fit for Microsoft Fabric Apps. Rapid prototyping an idea going from a TypeScript file to a live, authenticated URL in minutes rather than after an infrastructure request. Internal tools and admin dashboards - approval queues, data-quality override screens, support-triage tools that need real write access and real auth, not just a report. Data exploration apps a custom frontend querying Fabric data via GraphQL when a standard Power BI report doesn't fit the interaction model. And AI agent backends, a governed, persistent-state data layer an agent can read and write against, rather than stateless function calls with no memory. Equally important: Microsoft is explicit that anything needing complex multi-step transactions, stored procedures, or a non-Entra-ID identity provider is out of scope today.

Microsoft Fabric Apps vs Traditional Web Dev, Power Apps, and Power BI Apps

The name invites confusion with adjacent tools, so it's worth being precise. A traditional web stack gives total control at the cost of building auth, hosting, and governance yourself. Power Apps is Microsoft's low-code platform for business users, typically reaching Fabric data through Dataverse virtual tables rather than owning the data directly. A "Power BI app" is an unrelated, older concept, a curated bundle of reports published to a workspace audience; it involves no backend logic at all. Fabric Apps sits between the first two: code-first and TypeScript-native, but with the infrastructure plumbing generated rather than hand-built.

DimensionTraditional Web DevPower AppsMicrosoft Fabric Apps
Primary audienceProfessional developersBusiness users / citizen developersTypeScript developers & AI coding agents
Data locationWherever you provision itDataverse (virtual tables into Fabric)Native OneLake / Fabric SQL database
Backend effortYou build auth, hosting, DB from scratchLargely abstracted, low-codeGenerated from TypeScript decorators
AuthenticationAny provider you integrateMicrosoft Entra ID / Dataverse securityFabric SSO (Entra ID) only in production
Transactions / stored procsFull supportSupported via connectors/flowsNot supported — app-layer compensation needed
Maturity (mid-2026)GA, mature ecosystemGA, mature ecosystemPublic preview — not for production-critical workloads yet

Best Practices, Common Mistakes, and Preview Limitations

Best practices

Keep foreign keys and relationship decorators explicit only where your code actually reads or writes them let the generator handle the rest. Push every schema change through code and rayfin up db apply, never through the portal's query editor. Treat @role() policies as mandatory on any entity holding user-specific data, written and reviewed at the same time as the entity itself, not bolted on afterward. Design around the absence of transactions from day one write idempotent, compensable operations rather than assuming multi-step writes are atomic.

Common mistakes

Editing the SQL database schema directly in the portal is the single most common cause of a broken subsequent deploy. Forgetting to register a new entity in schema.ts produces a client method that silently doesn't exist rather than a clear compile error. Shipping secrets or connection strings inside static frontend assets is a real risk, since that content is public by default. Assuming totalCount or count() exist on the client produces broken pagination UI late in development.

Current limitations (Preview)

Composite primary keys and custom key column names aren't supported. Many-to-many relationships require an explicit join entity. There's no count() method and totalCount isn't populated on paginated results. Authentication is locked to Fabric SSO in production, with no custom identity provider option. Complex multi-step transactions and stored procedures aren't supported. Regional availability isn't universal yet check Fabric's supported-regions documentation before committing a project to a specific tenant region.

Performance, Monitoring, and Maintenance

Fabric Apps draws from the same capacity unit (CU) pool as your Warehouses, Lakehouses, and notebooks, there's no separate billing surface, which simplifies cost tracking but also means an inefficient app competes with other workloads on the same capacity. Monitor consumption through the Fabric Capacity Metrics app rather than estimating it. On the query side: select only the fields you render, always paginate instead of fetching full tables, and pull related-entity fields into the same query via dotted paths instead of a per-row second round trip. Reference data that changes rarely is worth caching client-side rather than re-querying on every render, and any operation you know is transaction-sensitive should be logged with enough context to reconcile manually.

Frequently Asked Questions

Is Microsoft Fabric Apps generally available? No - it entered public preview at Microsoft Build 2026 (June 2, 2026), carrying supplemental preview terms, best-effort support, and no SLA. Hold production-critical workloads for GA.

Do I need to write GraphQL by hand? No. The generated RayfinClient exposes a fluent, type-safe API - select(), where(), orderBy(), that builds the query for you; the raw schema is still introspectable if you need it for a non-JS client.

Can I bring my own identity provider? No. Production apps authenticate exclusively through Fabric SSO (Microsoft Entra ID); email/password exists only for local development.

Can I run multi-step transactions? Not natively in preview model any operation that needs atomicity across multiple writes as an application-layer compensation pattern instead.

Where does application data physically live? In a Fabric-managed SQL database backed by OneLake, inside your own tenant and workspace - not in a third-party cloud account.

Microsoft Fabric Consulting
Scoping a Fabric Apps pilot or evaluating it against your existing architecture?
Numlytics works with engineering teams evaluating Microsoft Fabric migration and platform architecture who need to weigh new capabilities like Fabric Apps against existing investments in Power BI, Power Apps, and custom services.
Talk to a Certified Fabric Consultant →

Conclusion: Should You Build with Microsoft Fabric Apps?

Microsoft Fabric Apps closes a real architectural gap: the application layer that data teams have always built outside the governed estate, one way or another. For a developer, the appeal is a single TypeScript codebase generating a full backend. For an architect, the appeal is that the resulting data never leaves the estate you already govern. The tradeoffs are real too, no transactions, no custom auth, no composite keys and worth treating as hard constraints in a scoping conversation rather than details to discover mid-build.

Numlytics works with engineering teams evaluating Microsoft Fabric migration and platform architecture who need to weigh new capabilities like Fabric Apps against existing investments in Power BI, Power Apps, and custom services. If you're scoping a pilot, talk to a certified Fabric consultant before committing a production workload to a preview feature.