定義 Fabric 應用程式的資料模型

Fabric Apps 使用 TypeScript 裝飾器來定義產生資料庫資料表與 API 的資料模型。 你將每個實體定義為使用 @entity() 裝飾的類別,為資料型別新增欄位裝飾器,並對應實體之間的關係。

關於授權與存取控制的指引,請參閱 定義資料權限

先決條件

  • 一個使用 npm create @microsoft/rayfin@latest 建立或以 npx rayfin init 初始化的 Fabric Apps 專案。
  • 對 TypeScript 類別和裝飾工具的基本了解。

定義實體

要建立資料模型,請將 decorator 加入 @entity() TypeScript 類別。 接著從 @microsoft/rayfin-core 匯入所需的裝飾器:

import { entity, uuid, text, date } from '@microsoft/rayfin-core';

@entity()
export class Todo {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) description?: string;
  @date() createdAt!: Date;
  @date() updatedAt!: Date;
}

此實體會生成一個 Todo 表格,其中包含 idtitledescriptioncreatedAtupdatedAt 欄位。

主鍵

每個實體都使用一個 string UUID id 欄位作為其主鍵。 如果你沒有明確宣告 id,Fabric Apps 會自動把它加入 schema。

  • 在建立操作中,該 id 欄位是可選的——如果你省略,伺服器會產生 UUID。
  • 如果你偏好客戶端產生的識別碼,也可以在建立時自行提供 UUID。
  • 不支援複合主鍵和自訂鍵名。
@entity()
export class Note {
  @uuid() id!: string;  // UUID primary key, auto-generated when omitted
  @text() title!: string;
  @text() content!: string;
}

支援的數據類型

使用這些裝飾器來定義欄位類型:

裝飾項目 類型 Description
@uuid() 字串 唯一識別碼欄位。
@text() 字串 具有可選長度限制的文字欄位。
@int() 編號 整數場。
@decimal() 編號 十進位欄位或數值欄位。
@boolean() 布爾值 真偽欄位。
@date() Date 日期與時間欄位,從 ISO 字串或 Date 物件序列化。
@email() 字串 帶有電子郵件驗證的文字欄位。
@set() 字串 列舉的字串文字集合。

包含多種類型的範例

import { entity, uuid, text, int, decimal, boolean, date, set } from '@microsoft/rayfin-core';

@entity()
export class Product {
  @uuid() id!: string;
  @text() name!: string;
  @decimal() price!: number;
  @int() stockQuantity!: number;
  @boolean() isAvailable!: boolean;
  @date() createdAt!: Date;
  @set('draft', 'published', 'archived') status!: 'draft' | 'published' | 'archived';
}

型態修飾符

在現場裝飾器中加入修飾符以設定驗證與約束:

修飾符 Description
{ optional: true } 允許 NULL 值。 欄位預設為必填。
{ unique: true } 新增一個唯一的限制條件。
{ default: value } 設定預設值表達式。
{ max: n }{ min: n } 字串長度限制(字元數上限與下限)。
{ min: n }{ max: n } 數值限制。

備註

TypeScript 的可選標記(? 在屬性名稱後)只影響靜態 TypeScript 類型。 它不會讓資料庫欄位變成可空的。 若要讓欄位可為 null,請在裝飾器中加入 { optional: true }。 用 ! 來斷言框架初始化了一個必填欄位。

帶有修飾符的範例

@entity()
export class User {
  @uuid() id!: string;
  @email({ unique: true }) email!: string;
  @text({ min: 3, max: 50 }) username!: string;
  @text({ optional: true, max: 500 }) bio?: string;
  @int({ min: 0, max: 150 }) age!: number;
  @boolean({ default: false }) isVerified!: boolean;
}

定義關聯性

使用 @one()@many() 裝飾工具來定義實體間的導航屬性。 當你定義關係時,Fabric Apps 會自動產生外鍵欄位。

  • 一對多——在父項上使用 @many(),並在子項上使用 @one()
  • 多對一-在子項上使用 @one() 來參照父項。
  • 不支援多對多關係——請改用明確的聯結實體。

一對多關係範例

import { entity, uuid, text, date, one, many } from '@microsoft/rayfin-core';

@entity()
export class Notebook {
  @uuid() id!: string;
  @text() name!: string;
  @date() createdAt!: Date;
  @many(() => Note) notes?: Note[];
}

@entity()
export class Note {
  @uuid() id!: string;
  @text() title!: string;
  @text() content!: string;
  @date() createdAt!: Date;
  @text() notebook_id!: string;
  @one(() => Notebook) notebook?: Notebook;
}

當你在 @one(() => Notebook) 實體上定義 Note 時,Fabric Apps 會自動建立一個 notebook_id 外鍵欄位。 只有當你打算在應用程式碼中讀取或設定外鍵欄位時,才要明確宣告外鍵欄位。

外鍵命名慣例

當你定義外鍵欄位時,請使用以下 {property}_id 命名規則:

@entity()
export class Note {
  @uuid() id!: string;
  @text() notebook_id!: string;      // Foreign key field
  @one(() => Notebook) notebook?: Notebook;  // Navigation property
}

不支援自訂的外鍵名稱(foreignKeytargetKey 選項)。

參考系統實體

Fabric Apps 不支援指向系統實體的 @one() 關係,例如內建的 USER 實體。 要將資料列與已登入的使用者建立關聯,請新增一個一般的 user_id 欄位,類型為 @text(),並使用驗證宣告中的值填入(通常為 claims.sub):

@entity()
export class Task {
  @uuid() id!: string;
  @text() title!: string;
  @text() user_id!: string;  // System user ID from claims.sub
}

在角色政策中依據 user_id 篩選資料列,以強制執行每位使用者的存取控制。

在結構中登錄實體

將所有實體類別加入 rayfin/data/schema.ts,讓用戶端能產生 GraphQL Proxy:

import type { Note } from './Note.js';
import type { Notebook } from './Notebook.js';

export type NotesAppSchema = {
  Note: Note;
  Notebook: Notebook;
};

每次建立新實體時,都會更新這個類型。

套用結構描述變更

定義或修改實體後,將變更套用到資料庫中:

  1. 將更新後的結構部署到 Fabric:

    npx rayfin up db apply
    
  2. 如果結構變更包含破壞性操作(刪除欄位、重新命名資料表),CLI 會警告你並拒絕繼續。 使用 --force 來略過安全檢查:

    npx rayfin up db apply --force
    

備註

使用 --force 可能會導致資料遺失。 在進行前,請仔細審查列出的操作內容。

最佳做法

  • 只有在需要讀取或設定程式碼時才定義外鍵欄位——Fabric Apps 會自動由導航裝飾器產生。
  • 在實體檔案中使用含有 .js 副檔名的相對匯入,讓產生的 ESM JavaScript 能夠正確解析。
  • 關於授權模式與基於角色的存取,請參見 定義資料權限

故障排除

缺少的關聯

如果關係未顯示在 API 中,請確認以下事項:

  • 導航裝飾器(@one()@many())也在場。
  • 該實體註冊於 rayfin/data/schema.ts