<< All versions

Skill v1.0.0

currentTrusted Publisher100/100
microsoft/rayfin/data-modeling
──Details
PublishedOctober 10, 2026 at 08:43 AM
Content Hashsha256:be5d62e9266fb328...
Git SHA
──Files
Files (1 file, 7.9 KB)
SKILL.md7.9 KBactive
SKILL.md · 167 lines · 7.9 KB

version: "1.0.0" name: data-modeling description: > Use when the app needs to store or read data — records, a database, entities, CRUD, lists, "save/remember X", or per-user data with row-level security. This app's data layer is Rayfin's: you declare entities as TypeScript classes with decorators in packages/data/src/, register them in the schema, and read/write them through the typed rayfin-client. Covers: defining an @entity, field decorators, registering it, per-entity access with @role, owner-only row-level security, and the client query/mutation API. Triggers: data, database, model, entity, schema, table, record, CRUD, create, read, update, delete, list, store, save, persist, per-user, row-level security, RLS, access control, ownership.


Data modeling — entities, schema, and row-level security

This app persists data with Rayfin data. You define entities as decorated TypeScript classes under packages/data/src/, register them in packages/data/src/index.ts, define their isomorphic contracts in packages/shared/src/index.ts, and read/write them with the typed client from packages/frontend/src/lib/rayfin-client.ts (await getRayfinClient()).

Read the docs first — they ship in the packages

Rayfin's data rules are owned by the installed packages, not by this skill. Decorators, field options, relationships, permissions, the query API and the platform's constraints all move between releases. Read them from disk:

You need…Read
@entity, every field decorator, and their optionsnode_modules/@microsoft/rayfin-core/assets/docs/decorators.md
Permissions end to end — @role, the policy DSL, claims, include / excludenode_modules/@microsoft/rayfin-guide/assets/docs/data/permissions.md
The @anonymous / @authenticated shorthandsnode_modules/@microsoft/rayfin-core/assets/docs/permissions.md
Entity authoring end to end, relationships, schema registrationnode_modules/@microsoft/rayfin-guide/assets/docs/data/overview.md
Reading and writing with the clientnode_modules/@microsoft/rayfin-guide/assets/docs/data/graphql.md
Paging, cursors, text-field nullabilitynode_modules/@microsoft/rayfin-data/assets/docs/index.md
Constraints that will bite you — text length, FK naming, many-to-many, dialectnode_modules/@microsoft/rayfin-guide/assets/docs/known-limitations.md

Read known-limitations.md before you design entities. It is short, and it is where the platform tells you what it will not do.

Everything below is about this template — what it turns on, where files go, and how its build is wired. For anything about Rayfin itself, use the table.

Step 1 — turn the data service on

The base template keeps the data service off, because a dashboard over a semantic model does not need a database. Turn it on for an app that needs persistence:

sh
npm run pack:add -- data-modeling

That flips data.enabled in rayfin/rayfin.yml and plants a starter entity in packages/data/src/Item.ts, registered in packages/data/src/index.ts, with its isomorphic contract in packages/shared/src/index.ts. Re-applying is safe: it keeps files you have edited, and --force-seeds is the explicit way back to the pack's version.

The template authenticates before rendering app content through its dynamic auth provider and gate. Keep that boundary intact and follow the installed rayfin-guide/assets/docs/auth/ pages for additional service-specific requirements. Preserve server-side entity permissions and row-level security. Do not replace an authentication failure with anonymous access or block the entire app because it is outside the Fabric portal.

Step 2 — write entity files

One file per entity under packages/data/src/, for example packages/data/src/Note.ts. Start from the seeded Item.ts — rename it and change the fields. Editing a working entity is more reliable than writing one from nothing. When the real model replaces the example, remove the unused starter Item runtime entity, its packages/data/src/index.ts registration, and its packages/shared/src/index.ts contract so it does not become user-visible. @microsoft/rayfin-core, @microsoft/rayfin-data and @microsoft/rayfin-client are already pinned in the base app — nothing to install.

For the decorator set, field options, relationship syntax, FK naming and the many-to-many rule, read rayfin-core/assets/docs/decorators.md and rayfin-guide/assets/docs/data/overview.md, then known-limitations.md.

Step 3 — register entities in this template's schema

packages/data/src/index.ts starts with the seeded Item registration, while packages/shared/src/index.ts owns its isomorphic record contract and schema type map. Add every entity, including join entities, to both packages:

ts
// packages/shared/src/index.ts
export interface NoteRecord {
id: number;
title: string;
}
export type UniversalAppSchema = {
Note: NoteRecord;
};
// packages/data/src/index.ts
import { Note } from './Note.js';
export const schema = [Note];
The client's generic type comes from UniversalAppSchema (already referenced by
packages/frontend/src/lib/rayfin-client.ts), so add every entity to both the
schema array: one registered in the array but missing from the type is
reachable at runtime and invisible to the compiler.
Type-check with `tsc -b`, never `tsc --noEmit`. The workspace packages are
referenced TypeScript projects, and the frontend client imports the shared
schema type, so
a bare tsc --noEmit fails with TS6305 until the referenced project has been
built. Run npm run typecheck for the real referenced-project check.
npm run build uses tsc -b --noCheck for speed and does not prove the
entity types are correct.

Step 4 — read and write

The query chain, mutations, findById, filtering and paging are documented in rayfin-guide/assets/docs/data/graphql.md and rayfin-data/assets/docs/index.md. Use await getRayfinClient() from packages/frontend/src/lib/rayfin-client.ts inside an async query or mutation; that helper is this template's; the API it returns is the package's.

For the interface over your entities — optimistic updates, forms, and the loading / empty / error states — read `crud-ui`, which ships the kit.

Step 5 — per-user data

"Each person sees only their own …" is a server-side @role policy, never a filter in the UI. A client-side filter still leaves the rows fetchable.

Read rayfin-guide/assets/docs/data/permissions.md for the decorator, the typed policy DSL, the supported claims, logical composition, and field visibility — it is the fullest treatment. rayfin-core/assets/docs/permissions.md adds the @anonymous / @authenticated shorthands, owner-field typing, and owner stamping. Keep authentication and authorization on the data operations that require them.

Notes

  • Stable features only. Use @authenticated / @role('authenticated', …).

Anonymous data access on Fabric requires a tenant admin switch — see rayfin-guide/assets/docs/data/overview.md. If asked for public data, say so and use authenticated access.

  • Deploying ships the data model. A schema change migrates on the next

deploy. Read app-deployment and follow the installed, version-matched CLI guide.

<!-- MAINTAINERS: this skill owns this template — the data service toggle, the workspace schema shape, the tsc -b build note, and the auth pairing. It does not own Rayfin.

Rayfin's own rules are read at runtime from the installed packages' docs (rayfinDocs.dir, today assets/docs), so they cannot drift from the version a generated app actually has. That replaces the previous arrangement, where this file hand-mirrored rules from the CLI's bundled skill:rayfin and had to be diffed against it on every CLI bump. Drift under that arrangement had already shipped once: bare @text() appeared in every example here while the authoritative rule required an explicit max on MSSQL. -->

All versions