Rayfin

Field types

Complete reference for Rayfin's field decorators — @uuid, @text, @int, @decimal, @boolean, @date, @email, @set, and @blob — and the options each accepts.

Every property on a @entity() class needs exactly one field decorator. The decorator determines the database column type, the GraphQL scalar, and the constraints Rayfin enforces for you. This page lists every decorator, the options it accepts, and the patterns that trip people up — especially the MSSQL text length rule.

Decorator reference

DecoratorLogical typeExtra options beyond the common setNotes
@uuid()UUIDConventionally the primary key (id). Also used for foreign key columns.
@text()stringmax, min, regexAlways set max on MSSQL — see below.
@int()integermax, minWhole numbers.
@decimal()decimal / numericmax, min, precision, scaleDefaults to DECIMAL(18,2).
@boolean()booleanTrue/false.
@date()datetimeAccepts a Date, an ISO string, or a numeric timestamp on write; serializes as ISO on read.
@email()stringmax, min, regexText field with email-shaped validation. Same option set as @text().
@set(...values)string enumA fixed list of allowed string literals.
@blob()A class decorator for storage folders, not a field type. See below.

Every field decorator except @blob() also accepts the common options in the next section.

Common options

These apply to @uuid(), @text(), @int(), @decimal(), @boolean(), @date(), @email(), and @set():

OptionTypeDefaultEffect
optionalbooleanfalseAllows NULL in the database. Pair with a ? on the TypeScript property — see below.
uniquebooleanfalseAdds a unique constraint on the column.
defaultmatches the field's typeDefault value used when the field is omitted on create.
descriptionstringFree-text note attached to the field's metadata. Does not change validation or the database column.
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, description: 'Owning user, set from claims.sub on create' })
  user_id!: string;
}

The nullable pattern

Fields are required by default. To make a field nullable, you must do two things together — set { optional: true } in the decorator and mark the TypeScript property with ?. Either one alone is not enough for consistent behavior between the database constraint and the generated types.

rayfin/data/Todo.ts
@text() title!: string;                    // required
@text({ optional: true }) notes?: string;   // nullable — both the option and `?` are present

Text length and MSSQL

Warning

On MSSQL, a @text() field without max generates an NVARCHAR(MAX) column. Rayfin's metadata provider can fail to build a GraphQL schema from NVARCHAR(MAX) columns, producing an "Internal server error" at runtime — after rayfin up has already reported success. Always set max on every @text() field: @text({ max: 200 }). This applies to @email() too, since it shares @text()'s option set.

There is no safe default length to omit — pick a max that fits the data (50 for a short name, 2000 for a description, and so on). If a deploy succeeds but GraphQL queries against a new or changed entity start failing, check every @text() field on that entity for a missing max first.

@decimal() precision and scale

precision is the total number of digits (before and after the decimal point); scale is the number of digits after it. They must be provided together — if you set one, set the other. Omit both to get the default, DECIMAL(18,2). Maximum precision is 28, a limit imposed by the Data API Builder runtime.

rayfin/data/Product.ts
import { entity, authenticated, uuid, decimal } from '@microsoft/rayfin-core';

@entity()
@authenticated('*')
export class Product {
  @uuid() id!: string;
  @decimal() price!: number;                       // DECIMAL(18,2) by default
  @decimal({ precision: 10, scale: 4 }) weight!: number; // DECIMAL(10,4)
}

@set() string enums

@set() takes the allowed values as separate string arguments, and the TypeScript union type should match:

rayfin/data/Todo.ts
@set('low', 'medium', 'high') priority!: 'low' | 'medium' | 'high';

To add common options (optional, unique, default) to a set field, pass an options object as the first argument instead, followed by the allowed values:

rayfin/data/Todo.ts
@set({ optional: true }, 'low', 'medium', 'high') priority?: 'low' | 'medium' | 'high';

@blob() storage folders

Warning

Storage is experimental and is not available in every Fabric region or tenant. See Storage before you depend on it.

@blob() is a class-level decorator, structurally different from the field decorators above — it marks a class as a storage folder configuration for @microsoft/rayfin-storage, the same way @entity() marks a class as a database table. It does not take optional, default, max, or unique options itself, and the properties inside the class are plain TypeScript fields rather than @text() / @uuid() decorated columns:

rayfin/storage/ProfileImage.ts
import { blob, role } from '@microsoft/rayfin-core';

@blob('uploads')
@role('authenticated', '*', {
  policy: (claims, item) => claims.sub.eq(item.owner_id),
})
export class ProfileImage {
  owner_id!: string;
}

The string argument ('uploads' above) is the storage folder name; it defaults to the kebab-case class name if omitted. Permissions on a @blob() class use the same @role() / @anonymous() / @authenticated() decorators described in Permissions and row-level security, but they generate a storage policy instead of a database policy. File upload, download, and listing operations are part of the storage client, not the client.data.<Entity> API this section covers.

PromptAdd a validated field to an entity
In my Rayfin project, add an "email" field to the User entity in rayfin/data/User.ts using the @email() decorator with max: 254 and unique: true. Make sure the TypeScript property is required (no `?`), since the field should not be nullable.
Something wrong on this page?Report an issueEdit this page

On this page