Microsoft Fabric Apps + Rayfin: The Open-Source BaaS That Turns Fabric Into an Application Platform
At Microsoft Build 2026, Microsoft introduced something that quietly shifts the Fabric conversation from "analytics platform" to "full application platform." The piece is Rayfin — a new, open-source SDK and CLI that lets you describe a complete application backend in TypeScript and ship it to Microsoft Fabric in a single command. It is MIT-licensed, currently in public preview, and the repo is already at 590 stars and 62 forks as of late August.
If you have been stitching together Cosmos DB, App Service, Entra ID, APIM, and OneLake just to give an AI agent a writeable data plane, this is the announcement that lets you stop doing that. Let me show you what Rayfin is, what it actually replaces, and how to ship your first app.
The Gap Rayfin Closes
AI coding agents can scaffold a clean frontend in seconds. The backend is still where the work piles up. The moment your app needs real data, you are stitching together a database, an identity provider, access policies, hosting, and then reconciling all of that with the governance and compliance rules your organization already enforces. That is the gap between a prototype that demos well and a system you can actually run. Prototypes are easy to build and hard to scale.
Rayfin exists to close that gap. You describe your data model with TypeScript decorators, and Rayfin provisions and manages the backend for you: database, authentication, data APIs, storage, and hosting. Because the deployment target is Microsoft Fabric, application data lands directly in OneLake — unified with analytics, operational data, real-time data, and AI engines by default, and inheriting Fabric's governance, security, and compliance from day one.
Microsoft positioned it as a way to "move from prompt to production without managing infrastructure." The Replit partnership — confirmed by Amjad Masad in the same launch — makes the same point: agents write the code, Fabric ships it.
What Rayfin Actually Is
Rayfin is a fully managed Backend-as-a-Service (BaaS) runtime that runs on Microsoft Fabric. Think Firebase on Google Cloud, or Supabase on PostgreSQL — but built natively on the Fabric data estate, with the Microsoft Learn docs explicitly calling it "Backend-as-a-Service with built-in auth, data, and more."
The full surface area:
- Decorator-driven data models. You define entities as TypeScript classes with
@entity(),@text(),@int(),@uuid(),@date(),@boolean(),@decimal(),@email(), and@set(). Rayfin generates the table, the GraphQL API, and the type-safe client. - Auto-generated GraphQL + REST. No hand-written controllers, no DTO classes. The schema is the API.
- Authentication built in. Auth is mandatory for Fabric deployments (turning it off causes
npx rayfin upto fail). Fabric SSO, Entra ID, and local password auth for dev are all supported via a single YAML config. - OneLake storage with native governance. Application data lands in your tenant's OneLake, with the same lineage, access controls, and audit logging as your Power BI datasets.
- Agent-friendly by design. The repo ships dedicated plugins for Claude Code (
.claude-plugin/), GitHub Copilot (.github/), Cursor (.cursor-plugin/), Codex (.codex-plugin/), and others. Coding agents can scaffold, modify, and deploy a complete backend from a natural-language spec. - MIT licensed. Fork it, vendor it, audit it, ship it on-prem if you ever need to.
The GitHub repo is at github.com/microsoft/rayfin under MIT. The Microsoft product page is at aka.ms/rayfin, and the official Microsoft Learn docs live under learn.microsoft.com/en-us/fabric/apps/.
How It Compares to Alternatives
| Capability | Rayfin (Fabric Apps) | Power Apps | Azure App Service + Cosmos | Supabase |
|---|---|---|---|---|
| **Data platform** | OneLake (Fabric) | Dataverse | Cosmos DB (your choice) | PostgreSQL |
| **Governance** | Native Fabric | Dataverse DLP | Manual | Manual |
| **Auth** | Entra ID + Fabric SSO | Entra ID | Bring your own | Supabase Auth / OIDC |
| **Open source** | Yes (MIT) | No | No | Yes (Apache 2.0) |
| **Code-first** | TypeScript decorators | Low-code | Any language | SQL + TypeScript SDK |
| **Agent-Ready** | First-class plugins | No | No | Partial |
| **Cost model** | Fabric FPU | Per-user licensing | App Service plan + RU/s | Free tier + paid |
| **Maturity** | Public preview (June 2026) | GA | GA | GA |
The interesting row is governance. With Supabase or a hand-rolled App Service + Cosmos stack, you have to manually replicate OneLake's access controls, lineage, and audit logging for the app's data. With Rayfin, that work disappears — the data lands in OneLake and the governance model is inherited automatically.
The Data Model: How It Actually Works
The programming model is decorator-driven, not function-call-driven. The Microsoft Learn data-models doc is the source of truth here. Every entity uses a UUID id as its primary key (auto-generated if you don't supply it), and you compose fields with the typed decorators.
import {
entity, uuid, text, int, decimal, boolean, date, email, set
} from '@microsoft/rayfin-core';
@entity()
export class Product {
@uuid() id!: string;
@text({ min: 1, max: 200 }) name!: string;
@text({ optional: true, max: 2000 }) description?: 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';
}
@entity()
export class Customer {
@uuid() id!: string;
@email({ unique: true }) email!: string;
@text({ min: 3, max: 50 }) username!: string;
@int({ min: 0, max: 150 }) age!: number;
@boolean({ default: false }) isVerified!: boolean;
}
Relationships use @one() and @many() navigation decorators. Many-to-many is not supported — you build an explicit join entity instead.
import { entity, uuid, text, date, one, many } from '@microsoft/rayfin-core';
@entity()
export class Notebook {
@uuid() id!: string;
@text() name!: string;
@date() createdAt!: Date;
@many(() => Note) notes?: Note[];
}
@entity()
export class Note {
@uuid() id!: string;
@text() title!: string;
@text() content!: string;
@date() createdAt!: Date;
@text() notebook_id!: string;
@one(() => Notebook) notebook?: Notebook;
}
A few rules worth burning in:
- The TypeScript
?optional marker does not make the column nullable. Use{ optional: true }on the decorator. The docs are explicit about this. - Foreign key fields (
notebook_idabove) only need to be declared if you read or set them in code. The framework auto-generates them from the navigation decorators. - Use relative imports with
.jsextensions in entity files so the emitted ESM JavaScript resolves correctly. - Every entity has to be registered in
rayfin/data/schema.tsor the API will not see it.
Authentication and Permissions
Auth is a first-class YAML config, not a custom service. From the Microsoft Learn authentication doc:
services:
auth:
enabled: true
allowedRedirectUris:
- http://localhost:5173
fabric:
enabled: true # Fabric SSO (Entra ID) for the workspace
password:
enabled: true # Local username/password — dev only
Key facts:
- Setting
services.auth.enabledtofalsecausesnpx rayfin upto fail. There is no off switch for production. - For production, you want
fabric.enabled: true(Entra ID / Fabric SSO) andpassword.enabled: false. - The session is exposed to your code as
user.idanduser.email— that is the whole identity surface area. - Row- and column-level permissions are configured in a separate
data-permissionslayer (see the Microsoft Learn link above).
Deploying to Fabric
The day-to-day loop is short. From the Build 2026 lab materials and the Microsoft Learn deploy docs:
# Scaffold a new project
npm create @microsoft/rayfin@latest
# Or initialize in an existing folder
npx rayfin init
# Local dev (live reload, SQLite under the hood)
npx rayfin dev
# Sign in with Entra ID the first time you deploy
npx rayfin login
# Deploy to Fabric — provisions backend, runs migrations, applies permissions
npx rayfin up
# Health check
npx rayfin dev status
A plain npx rayfin up does more than just push code. It redeploys the backend and runs any needed schema migration in one shot. There is a safety edge worth knowing about: if a migration would drop a non-empty column, the CLI will prompt before proceeding. In CI you pass --yes to skip the prompt.
Practical Walkthrough: A Customer Support Portal
Let's put it together end-to-end. The scenario: a small B2B SaaS company wants a support portal where customers can open and track tickets. Today this is a TypeScript/React app on App Service with Cosmos DB and Entra ID. With Rayfin, the data layer and auth collapse to a handful of files.
1. Scaffold and define models
npm create @microsoft/rayfin@latest support-portal
cd support-portal
// rayfin/data/schema.ts
import { entity, uuid, text, date, set, one, many } from '@microsoft/rayfin-core';
import { Customer } from './customer.js';
import { SupportTicket } from './ticket.js';
export const schema = [Customer, SupportTicket];
// rayfin/data/customer.ts
import { entity, uuid, text, email, set, many } from '@microsoft/rayfin-core';
import { SupportTicket } from './ticket.js';
@entity()
export class Customer {
@uuid() id!: string;
@email({ unique: true }) email!: string;
@text() companyName!: string;
@set('free', 'pro', 'enterprise') tier!: 'free' | 'pro' | 'enterprise';
@date() createdAt!: Date;
@many(() => SupportTicket) tickets?: SupportTicket[];
}
// rayfin/data/ticket.ts
import { entity, uuid, text, date, set, one } from '@microsoft/rayfin-core';
import { Customer } from './customer.js';
@entity()
export class SupportTicket {
@uuid() id!: string;
@text() subject!: string;
@text({ max: 5000 }) description!: string;
@set('open', 'in_progress', 'resolved', 'closed')
status!: 'open' | 'in_progress' | 'resolved' | 'closed';
@date() createdAt!: Date;
@date() updatedAt!: Date;
@text() customer_id!: string;
@one(() => Customer) customer?: Customer;
}
2. Wire up auth
# rayfin/rayfin.yaml
services:
auth:
enabled: true
allowedRedirectUris:
- http://localhost:5173
- https://support.example.com
fabric:
enabled: true
password:
enabled: false # production — Entra ID only
3. Deploy
npx rayfin login # one-time Entra ID sign-in
npx rayfin up # provisions backend, runs migration, returns the GraphQL endpoint
What you get back: a GraphQL endpoint with full CRUD on Customer and SupportTicket, type-safe clients you can generate for any frontend, OneLake storage with lineage back to Power BI, audit logging on every read/write, and Entra ID auth for the front door. The whole thing is a few hundred lines of TypeScript and one YAML file.
When Rayfin Makes Sense
- You are already on Fabric. Your data is in OneLake, your governance is in Fabric, and you want to build applications that operate on the same data without ETL or a separate operational store.
- You need governed APIs fast. Instead of building an API tier, identity layer, and audit pipeline, you define entities and you get all three.
- Your team is using coding agents. The repo ships agent plugins for Claude, Copilot, Cursor, and Codex. A "build me a ticketing portal backed by Entra ID" prompt becomes a working deployable in hours, not weeks.
- You are a regulated Malaysian enterprise. Fabric inherits Bank Negara Malaysia, PDPA, and healthcare-sector compliance controls you have already set up. Rayfin apps get those controls for free.
When It Doesn't (Yet)
- Heavy compute or long-running jobs. Rayfin is CRUD-shaped. For CPU-intensive work (image processing, ML inference, video transcoding), use Azure Functions or Container Apps and call them from Rayfin.
- Real-time event streaming at scale. If your app ingests millions of events per second, Event Hubs + Eventhouse is still the right fabric pattern; use Rayfin for the application surface on top.
- Polyglot persistence. Rayfin data lives in OneLake. If your app needs a graph DB, a vector DB outside of Fabric's built-in AI services, or a third-party SaaS as a system of record, you are back to stitching.
- Anything that needs to be in production next Tuesday. It is public preview. The API, decorators, and migration behavior can still change. Pin your Rayfin version and budget migration time.
Implications for Malaysian Enterprises
For enterprises already invested in Fabric, Rayfin changes the conversation about application development in three concrete ways.
Data gravity. When your analytics, AI, and application data all live in OneLake, the data silos that plague most enterprises simply do not form. A customer portal built on Rayfin queries the same tables your Power BI dashboards use — no sync, no ETL, no drift. For Malaysian groups that are tired of paying twice for the same data (once in a transactional system, once in a warehouse), this is a real shift.
Governance without overhead. Malaysian enterprises in regulated industries — banking (BNM RMiT), healthcare (Ministry of Health guidelines), government (MyGov policies) — need governance that is enforced, not just documented. Rayfin inherits Fabric's governance model, so application data gets the same access controls, lineage tracking, and compliance reporting as analytical data. The Leatherman team said it best in the Build 2026 customer story: "We appreciate how quickly we can build and iterate in Replit, but for some of our business, our data needs to stay governed and centralized in Fabric. With Rayfin, we finally have both."*
Cost predictability. Rayfin runs on Fabric capacity units (FPU). If you are already paying for Fabric, the marginal cost of adding application workloads is incremental and rides on the same consumption model. No separate SaaS subscription, no per-user Power Apps licensing, no App Service plan on top of everything else.
Pitfalls to Watch
A few things I would not skip if I were rolling this out:
- Pin your Rayfin version. Public preview means the API can change. Use a specific version (
@microsoft/[email protected]) inpackage.json, notlatest. - Migration safety.
npx rayfin upruns migrations automatically. In CI, use--yesto skip the interactive prompts, but in interactive dev sessions, read the diff it shows you. Dropping a non-empty column is destructive. - Auth is mandatory. You cannot disable it. Plan your Entra ID app registration and redirect URIs before you start.
- No many-to-many. Use an explicit join entity. The framework will not auto-create one.
- The `?` optional marker lies. It is a TypeScript static-type thing, not a database-nullability thing. Use
{ optional: true }on the decorator or your column will be NOT NULL. - OneLake geography follows your Fabric capacity. Your Rayfin app's data residency is your Fabric capacity's residency. Pick the Fabric region that matches your data residency requirements up front — switching later is a migration, not a config change.
- No raw SQL escape hatch (yet). The generated GraphQL API is the surface. If you need stored procedures, complex joins the GraphQL schema cannot express, or bulk imports, use a Fabric SQL Database alongside the Rayfin app and wire them together with a Fabric data pipeline.
Getting Started
- Make sure Fabric Apps is enabled in your Fabric workspace. New workspaces created after Build 2026 have it on by default; older workspaces need a feature flag in the admin portal.
- Scaffold a project:
npm create @microsoft/rayfin@latest - Define entities in TypeScript using the decorators from
@microsoft/rayfin-core. - Register them in
rayfin/data/schema.ts. - Configure auth in
rayfin/rayfin.yaml— Entra ID for production. - Sign in:
npx rayfin login - Deploy:
npx rayfin up - Wire your frontend to the generated GraphQL endpoint and ship.
The Microsoft Learn quickstart walks through the same flow with the Todo sample. The Build 2026 lab repo has a field-services app you can clone and deploy end-to-end.
Key Takeaways
- Rayfin is Fabric's application layer. It turns Fabric from an analytics-only platform into a full application platform with governed APIs, auth, and data models — provisioned by declaring a TypeScript schema.
- MIT-licensed and agent-first. The SDK is open source on GitHub under MIT and ships dedicated plugins for Claude Code, GitHub Copilot, Cursor, and Codex. Coding agents can scaffold and deploy a complete backend from a natural-language spec.
- Data gravity is the real value. Application data in OneLake means no ETL, no sync, and unified governance across analytics and applications. The data is in one place from day one.
- Public preview — pin versions. The API is stabilizing but not stable. Pin your
@microsoft/rayfin-coreversion, budget time for migration changes, and don't go to production at scale before GA. - Start with governed CRUD apps. Customer portals, internal tools, partner-facing apps, and data-driven line-of-business apps are the sweet spot. Heavy compute, real-time streaming, and polyglot persistence still belong on Functions, Event Hubs, and Cosmos — and Rayfin can call them.
If you are an enterprise architect in Malaysia evaluating where to put the next internal app, my honest read is this: the question is no longer "Can we build it on Azure?" It is "Should the data live in OneLake and be served from Fabric, or should it live somewhere else?" For most of the apps I see on customer workshops — portals, ticketing, approval flows, partner integrations — the answer is now the former, and Rayfin is the path of least resistance to get there.