An introduction to Fabric Apps
A hands-on walkthrough of creating, deploying, and understanding Microsoft Fabric Apps.
One of the shiny new items in Microsoft Fabric is Fabric Apps, and these open up a bunch of possibilities for what is possible in Fabric. Fabric Apps enabled functionality that previously would have needed to be custom workloads, which are a lot more complex to get up and running than Fabric Apps.
What is a Fabric App?
A Fabric app (built on the Rayfin SDK) is a Fabric item with two components, a backend service for data access and authentication, and a front end web app. When a Fabric App is created, Fabric automatically creates a SQL Database, an authentication server, and static content hosting for the front end web app. You provide data models for your application in TypeScript and Fabric Apps automatically creates a database schema and type-safe GraphQL API.
Before you can create one, a tenant admin has to enable it. It's still in preview, so it needs to be explicitly enabled, and Fabric Apps are not available in all regions.
Fabric Apps are currently available in the following regions (As of August 18th, 2026):
- US - Central US
- US - North Central US
- US - West US
- US - West US 2
- Europe - West Europe
- France Central
- Italy North
- Norway East
- Switzerland North
- UAE North
- South Africa North
- Asia - East Asia
- Asia - Southeast Asia
- Australia East
- India - Central India
- Japan East
- Korea Central
To enable the preview in your tenant:
- Sign in to the Fabric admin portal.
- Go to Tenant settings.
- Under Enable Fabric App Items (preview), toggle it to Enabled, scoped to your whole org or specific security groups.
- Select Apply. Give it a few minutes to propagate.
If you don't see App in your New item list, either this is why or your capacity is in an unsupported region.
Part 1: Create and Deploy the Sample App
Create the item in Fabric
Creating the item itself is the same as any other Fabric item:
- Open a workspace where you have contributor or higher access.
- Select New item.
- Search for App (preview), select it, give it a name, and select Create.
This creates the item, and all the backend services I mentioned before. From here, you can select a blank app, or start with a sample To-Do App, or a sample Data App.
For the sake of this post, I am going to select the To-Do App.
Once selected, Fabric will start to deploy your app and present you with some instructions for getting the Rayfin CLI up and running.
Create the project with npm
From a terminal, run the command shown in the Fabric App:
npm create @microsoft/rayfin@latest -- "<appitemname>" --template todoapp --workspace <workspacename>
That one command creates a full project from the todoapp template and wires it to the workspace and item you just created, using the Rayfin CLI.
Then change your working directory to the project directory that was just created
cd <your project directory>
Run it locally
npm run dev
This spins up the frontend and backend together against your Fabric backend, so you can test changes before anything goes live. By default it runs at http://localhost:5173.
Deploy with npx
npx rayfin up
Under the hood, rayfin up does six things in order:
- It creates (or reuses) the Fabric App item
- It retrieves the publishable key
- It syncs your
rayfin.ymlsettings - It applies the database schema from your TypeScript models
- It builds and deploys static content,
- Finally, it writes the deployment details back to
rayfin.ymland a.env.fabric-<workspacename>file.
When it finishes, you get a live hosting URL, a Fabric portal link, and a deployment ID.
Want to check what a deploy will do without actually running it? Use npx rayfin up --dry-run. To check current deployment state at any time, use npx rayfin up status.
Part 2: The Database Objects
Here's the part that surprised me the most coming from a data background: there's no SQL to write, and no separate database designer to open. Your data models are TypeScript classes, and Fabric Apps turns them directly into database tables.
Defining an entity
Entities or tables live in rayfin/data/ and use the @entity() decorator from @microsoft/rayfin-core, as well as field decorators for each column:
Each table needs to be defined in it's own typescript file.
import { entity, uuid, text, boolean, date } from '@microsoft/rayfin-core';
@entity()
export class Todo {
@uuid() id!: string;
@text() title!: string;
@text({ optional: true }) description?: string;
@boolean({ default: false }) isComplete!: boolean;
@date() createdAt!: Date;
@date() updatedAt!: Date;
}
Every entity gets a UUID id primary key. If you don't include it in your entity definition, Fabric will automatically add it. When records are inserted into your table, Fabric will generated a UUID server-side unless you supply your own. Composite keys and custom key names aren't supported.
The full set of field decorators: @uuid(), @text(), @int(), @decimal(), @boolean(), @date(), @email(), and @set() for enumerated strings. Modifiers like { optional: true }, { unique: true }, { default: value }, and { min, max } add constraints to the columns.
The TypeScript ? optional marker only affects the compile-time type. To actually make a database column nullable, you need { optional: true } in the decorator itself.
Relationships
If your app has more than one table, use @one() and @many() to define relationships, and Fabric auto-generates the foreign key column for you following a {property}_id convention. One-to-many and many-to-one are supported; many-to-many is not, so model it with an explicit join table instead.
Registering the schema
Once every entity has been created, they then need to be added to rayfin/data/schema.ts:
import { Todo } from './Todo.js';
export type TodoAppSchema = {
Todo: Todo;
};
export const schema = [Todo];
Applying schema changes
Whenever you add or edit an entity:
npx rayfin up db apply
If a change would drop a column or rename a table, the CLI blocks it and warns you first. You can override with --force, but know that as soon as you add --force, you will be making a destructive change that cannot be undone.
Using --force on a schema apply can cause data loss and cannot be undone.
After being deployed, you are able to see the database in your workspace under the Fabric App. The SQL Database child item will allow you to access the Fabric SQL query editor where you can run SQL queries to read data. Don't change things in SQL here, as anything you change will be overwritten the next time you deploy the schema.
Part 3: Authentication
Authentication is built in to all Fabric Apps, making it easy to create secure applications. There are two auth methods depending on where your App is running:
- Local development - Login using your email and password
- Execution within Fabric - Fabric Single Sign On (SSO), where you use the identity of the logged-in user on their Microsoft Entra ID account.
These are configured in rayfin/rayfin.yml:
services:
auth:
enabled: true
allowedRedirectUris:
- http://localhost:5173
fabric:
enabled: true
password:
enabled: true # Local development only
services.auth.enabled has to be true for any Fabric deployment, npx rayfin up will fail if it's set to false.
When using Fabric Single Sign-On, there is no sign-up/registration step. When the user signs in for the first time, Fabric does everything it needs to in the background based on their Entra ID, simplifying one part of web development that people usually hate.
On the client, all of this lives under client.auth:
await client.auth.signOut();
client.auth.onSessionChange((session) => {
console.log(session?.isAuthenticated ? 'signed in' : 'signed out');
});
The sessions are opaque in nature, meaning that you're not intended to (and Microsoft has tried hard to prevent you) from trying to expose the underlaying tokens. Rayfin exposes an id, and email field for you to use in your app, abstracting away the complicated token handling. This is done using the same client object through which will automatically add the identity of the user to all data requests made.
ensureSignedInWithFabric() has to be called from a synchronous user-gesture handler, like a button's onClick. Call it on page load instead and the browser will block the sign-in popup.
Being signed in tells Fabric who someone is, it doesn't decide what they can see. That part is handled with a @role decorator on your entities:
@entity()
@role('authenticated', '*', {
policy: (claims, item) => claims.sub.eq(item.userId)
})
export class Todo {
@uuid() id!: string;
@text() title!: string;
@text() userId!: string;
}
That restricts every operation on Todo to rows where userId matches the signed-in user's sub claim, so people only ever see their own to-do items. There's also an anonymous role if part of your app needs to work without a sign-in.
Part 4: A Quick Look at the Front End
The front end lives in src/, and its structure depends on the template you picked when you created the project (React for the To-Do App). I won't go down the React rabbit hole here, that's a post (or actually an entire 4th year university course that still haunts my dreams) of its own, but it's worth knowing how the front end talks to your data.
Everything goes through RayfinClient, initialized once with your backend URL and publishable key:
import { RayfinClient } from '@microsoft/rayfin-client';
import type { TodoAppSchema } from '../rayfin/data/schema';
const client = new RayfinClient<TodoAppSchema>({
baseUrl: import.meta.env.VITE_RAYFIN_API_URL ?? 'http://localhost:5168',
publishableKey: import.meta.env.VITE_RAYFIN_PUBLISHABLE_KEY,
});
From there, reads, filters, sorts, pagination, and writes are all method calls against client.data.<Entity>:
const todos = await client.data.Todo
.select(['id', 'title', 'isComplete'])
.where({ isComplete: { eq: false } })
.execute();
The template wires up the Vite environment variables for you (VITE_RAYFIN_API_URL, VITE_RAYFIN_PUBLISHABLE_KEY, and after your first deploy, VITE_FABRIC_ITEM_ID and VITE_FABRIC_WORKSPACE_ID), so in the sample app you won't need to touch any of this, it's already there. It's just worth knowing where it lives for when you start adding your own entities and need to work with them.
If you only change front-end code and want a faster deployment than a full rayfin up, deploy just the static content:
npx rayfin up staticapp deploy
Wrapping Up
This to-do app is a great starting point for the Fabric Apps contest Builder track, running now through September 1 in the Discord User Group. If you don't have a full Fabric environment to build in, there is also a Concept track that only requires a mockup, so there's a way in either way. Drop questions in the Discord server, and I'll see the submissions there.
Microsoft Learn references: Create your first Fabric app | Fabric Apps project structure | Define data models for Fabric Apps | Authentication for Fabric Apps | Configure Fabric SSO authentication | Define data permissions | Read and write data with GraphQL in Fabric Apps | Deploy a Fabric app to Fabric
Join the Community
Connect with fellow Fabric enthusiasts on Discord. Get help, share insights, and stay up to date with the latest discussions.
Join Discord Server