---
title: "Data"
description: "Model entities once as decorated TypeScript classes and get a database schema, GraphQL API, type-safe client, permissions, and validation from the same source."
url: https://rayfin.ai/docs/data
markdown_url: https://rayfin.ai/docs/data.md
section: data
product: Rayfin
sdk_version: 1.34.0
cli_version: 1.33.2
last_updated: 2026-08-29T23:37:34-07:00
source: data/index.mdx
---

# Data

> Model entities once as decorated TypeScript classes and get a database schema, GraphQL API, type-safe client, permissions, and validation from the same source.

A Rayfin entity is a TypeScript class. Decorate it once and Rayfin generates the database
table, a GraphQL API, a type-safe client, row-level security, and form validation from
that single definition — there is no separate schema file, migration file, or API
contract to keep in sync by hand.

```typescript title="rayfin/data/Todo.ts"
import { entity, authenticated, uuid, text, boolean, date } from '@microsoft/rayfin-core';

@entity()
@authenticated('*', {
  policy: (claims, item) => claims.sub.eq(item.user_id),
})
export class Todo {
  @uuid() id!: string;
  @text({ max: 200 }) title!: string;
  @boolean({ default: false }) isCompleted!: boolean;
  @date() createdAt!: Date;
  @text({ max: 128 }) user_id!: string;
}
```

Register it in `schema.ts`, then apply it:

```bash
npx rayfin up
```

From there, `client.data.Todo` is a fully typed read/write API, scoped by the row-level
policy declared above.

## Set up the client [#set-up-the-client]

Construct one `RayfinClient` and reuse it across your app. Type it with your `AppSchema` so
every entity access is fully typed.

```typescript title="src/services/rayfinClient.ts"
import { RayfinClient } from '@microsoft/rayfin-client';
import type { AppSchema } from '../../rayfin/data/schema';

export const rayfinClient = new RayfinClient<AppSchema>({
  baseUrl: import.meta.env.VITE_RAYFIN_API_URL,
  publishableKey: import.meta.env.VITE_RAYFIN_PUBLISHABLE_KEY ?? '',
});
```

`VITE_RAYFIN_API_URL` and `VITE_RAYFIN_PUBLISHABLE_KEY` are generated into `.env.local` by
`rayfin up` — see [Schema changes](/docs/data/migrations) for the apply workflow that
produces them.

That one client also carries [`client.auth`](/docs/auth) and, when enabled,
[`client.functions`](/docs/functions/calling-functions). To additionally reach existing
Fabric data sources, swap it for `ConnectorsRayfinClient` — see
[Wiring connectors into your app](/docs/connectors/client-setup).

## Model your schema [#model-your-schema]

* **[Modeling entities](/docs/data/modeling)** — `@entity()`, file layout, primary keys,
  and the `schema.ts` registration step.
* **[Field types](/docs/data/field-types)** — every field decorator and its options,
  including the MSSQL text-length rule.
* **[Relationships](/docs/data/relationships)** — `@one()` / `@many()`, foreign key
  columns, and the many-to-many workaround.
* **[Permissions](/docs/data/permissions)** — `@role()`, `@anonymous()`,
  `@authenticated()`, and the row-level policy DSL.

## Read and write [#read-and-write]

* **[Querying](/docs/data/querying)** — the `select` / `where` / `orderBy` / `execute`
  chain, filtering, and pagination.
* **[Aggregations](/docs/data/aggregations)** — sums, averages, and counts computed on the
  server with `groupBy()` and `aggregate()`.
* **[Creating, updating, deleting](/docs/data/mutations)** — writes, and setting
  relationships correctly.
* **[Form validation](/docs/data/validation)** — generate form validation from the same
  entity, with no separate schema library.

## Operate [#operate]

* **[Schema changes](/docs/data/migrations)** — how edits to `rayfin/data/` reach the
  database, and how to verify they actually did.
* **[Seeding data](/docs/data/seeding)** — populate a database from a Node.js script.

```prompt title="Model your first entity"
In my Rayfin project, create a new entity in rayfin/data/ that models [describe your
data]. Give it a uuid id, appropriate field decorators from @microsoft/rayfin-core with
explicit max lengths on every text field, and an @authenticated('*') permission decorator
with a policy scoping rows to the signed-in user via claims.sub. Register it in
rayfin/data/schema.ts, then apply the schema with `rayfin up`.
```
