Skip to main content

data-modeling

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.

Source facts

Repository
microsoft/rayfin
Last source activity
October 8, 2026 at 20:46
Detected SKILL.md language
English
Stars
651
Forks
71

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
5 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
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 options | `node_modules/@microsoft/rayfin-core/assets/docs/decorators.md` | | Permissions end to end — `@role`, the policy DSL, claims, `include` / `exclude` | `node_modules/@microsoft/rayfin-guide/assets/docs/data/permissions.md` | | The `@anonymous` / `@authenticated` shorthands | `node_modules/@microsoft/rayfin-core/assets/docs/permissions.md` | | Entity authoring end to end, relationships, schema registration | `node_modules/@microsoft/rayfin-guide/assets/docs/data/overview.md` | | Reading and writing with the client | `node_modules/@microsoft/rayfin-guide/assets/docs/data/graphql.md` | | Paging, cursors, text-field nullability | `node_modules/@microsoft/rayfin-data/assets/docs/index.md` | | **Constraints that will bite you** — text length, FK naming, many-to-many, dialect | `node_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. -->
View on GitHub