Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Rayfin SDK uses a decorator-driven programming model where you define your data schema once in TypeScript and automatically receive production-ready APIs, type-safe clients, and infrastructure.
Key concepts
Rayfin SDK combines three core elements:
- Decorator-driven schema: Use TypeScript decorators to define data models, permissions, and relationships.
- Automatic API generation: Your decorated classes become GraphQL endpoints without writing controller code.
- Type-safe clients: Generated TypeScript clients provide compile-time validation for queries and mutations.
How it works
When you create a Fabric app, your code flows through these stages:
| # | Stage | What happens |
|---|---|---|
| 1 | Developer | You author the app in your editor of choice. |
| 2 | TypeScript | You write entity classes in TypeScript. |
| 3 | Decorators | You annotate classes and fields with @entity, @uuid, @text, @role, and other decorators. |
| 4 | Schema | The CLI compiles the decorated classes into a database schema, permission policies, and API configuration. |
| 5 | APIs | The schema is published as GraphQL endpoints. |
| 6 | Client | The generated RayfinClient exposes a type-safe data and auth client over those endpoints. |
| 7 | Application | Your frontend application consumes the client to read and write data. |
1. Define data models with decorators
You define your data structure using TypeScript classes and decorators from @microsoft/rayfin-core:
The default blankapp scaffold resolves to the Universal App workspace. Enable Data with npm run pack:add -- data-modeling, define entities in packages/data/src/, register runtime classes in packages/data/src/index.ts, and maintain client-facing contracts in packages/shared/src/index.ts. Flat templates can use rayfin/data/ and rayfin/data/schema.ts instead. Service paths in rayfin/rayfin.yml determine which layout the CLI builds.
import { entity, uuid, text, int } from '@microsoft/rayfin-core';
@entity()
export class Product {
@uuid() id!: string;
@text({ max: 200 }) name!: string;
@text({ optional: true, max: 2000 }) description?: string;
@int() price!: number;
}
2. Schema generation
The Rayfin CLI (npx rayfin) analyzes your decorated classes and generates:
- Database schema - Tables, columns, constraints, and indexes
- API configuration - GraphQL endpoint definitions
- Permission policies - Row-level security and field-level access control
4. Type-safe client usage
GraphQL APIs are available to perform CRUD operations on your database. Rayfin SDK provides a data client operation out of the box to read or write or delete data.
import { RayfinClient } from '@microsoft/rayfin-client';
import type { UniversalAppSchema } from '@rayfin-app/shared';
const client = new RayfinClient<UniversalAppSchema>({
baseUrl: import.meta.env.VITE_RAYFIN_API_URL,
publishableKey: import.meta.env.VITE_RAYFIN_PUBLISHABLE_KEY,
});
// Register Product in the data package and shared schema first.
const products = await client.data.Product
.select(['id', 'name', 'price'])
.execute();
// Compile-time error if field doesn't exist
const invalid = await client.data.Product
.select(['nonexistentField']) // ❌ TypeScript error
.execute();
Decorator reference
Rayfin SDK provides decorators for common data modeling patterns:
Entity decorators
| Decorator | Purpose | Example |
|---|---|---|
@entity() |
Mark a class as a database entity | @entity() class Product |
Property decorators
| Decorator | Database type | TypeScript type |
|---|---|---|
@uuid() |
UNIQUEIDENTIFIER | string |
@text({ max: n }) |
NVARCHAR(n) | string |
@int() |
INT | number |
@decimal() |
DECIMAL | number |
@bool() |
BIT | boolean |
@date() |
DATETIME2 | Date |
Permission decorators
| Decorator | Purpose |
|---|---|
@role() |
Define role-based permissions |
See Define data permissions for authorization details.
Functions
Create one UserDataFunctions instance, and register each function by calling udf.func(name, handler, connections?).
Follow these rules when you define a function:
- Place the
RayfinContextparameter anywhere in the handler signature. The runtime identifies it by type, injects it, and excludes it from the generated function inputs. - Declare external resource audiences in
RayfinContext<Schema, Audiences>and use the app identity's resource tokens throughctx.Tokens. Pass[]as the third argument toudf.func()when declaring those audiences. See Connect Functions to external resources.
The following example registers a plain function and a function that uses the invocation context:
import {
UserDataFunctions,
type RayfinContext,
} from '@microsoft/fabric-user-data-functions';
import type { UniversalAppSchema } from '@rayfin-app/shared';
const udf = new UserDataFunctions();
udf.func('greet', async (name: string): Promise<string> => {
return `Hello, ${name}!`;
});
udf.func(
'whoAmI',
async (ctx: RayfinContext<UniversalAppSchema>): Promise<string> => {
return ctx.accessToken ? 'authenticated' : 'anonymous';
},
);
The RayfinContext provides the function's runtime services:
| Member | Purpose |
|---|---|
ctx.accessToken |
Provides the signed-in user's Rayfin JSON Web Token (JWT). Decode the sub claim to get a stable user ID. |
ctx.Tokens.<Audience> |
Gets a platform-provided resource token for an audience declared in the RayfinContext annotation. Deployed Functions use the app identity and its permissions. For details, see Connect Functions to external resources. |
ctx.getSecret('NAME') |
Gets a secret for the current invocation. Returns undefined when the secret isn't set. |
ctx.getDataClient() |
Gets the typed data API for your entities. The client supports create, findMany, update, delete, and upsert operations. |
ctx.baseUrl |
Provides the Rayfin endpoint for the current item. |
ctx.publishableKey |
Provides the publishable key for the current item. |
For setup, local debugging, and deployment instructions, see Use Functions in Fabric Apps.
Development workflow
A typical development cycle follows this pattern:
- Define or modify data models - Add or update TypeScript classes with decorators
- Test locally with remote backend - Run
npm run devto test your frontend code changes against your App backend in Fabric. - Deploy to Fabric - Run
npx rayfin upto deploy to the managed Fabric service and applies your schema changes.
Changes to your TypeScript models automatically propagate through the entire stack — from database schema to API endpoints to client types.
Authorization
Permissions are defined alongside your data models using the @role decorator:
@entity()
@role('authenticated', ['create', 'read', 'update', 'delete'], {
policy: (claims, item) => claims.sub.eq(item.userId)
})
export class UserDocument {
@uuid() id!: string;
@text({ max: 255 }) userId!: string;
@text({ max: 4000 }) content!: string;
}
This approach ensures:
- Security rules live next to the data they protect
- Type-safe policy expressions catch errors at compile time
- Refactoring entity fields automatically updates permission checks