定义 Fabric 应用的数据模型

Fabric应用使用 TypeScript 修饰器来定义生成数据库表和 API 的数据模型。 将每个实体定义为修饰的 @entity()类,为数据类型添加字段修饰器,以及映射实体之间的关系。

有关授权和访问控制指南,请参阅 “定义数据权限”。

先决条件

  • 使用 npm create @microsoft/rayfin@latest 创建或使用 npx rayfin init 初始化的 Fabric Apps 项目
  • 基本了解 TypeScript 类和修饰器。

定义实体

若要创建数据模型,请将 @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 会自动将其添加到架构中。

  • 在创建操作期间,该 id 字段是可选的, 如果省略该字段,服务器将生成 UUID。
  • 如果更喜欢客户端生成的标识符,可以在创建时提供自己的 UUID。
  • 不支持复合主键和自定义键名称。
@entity()
export class Note {
  @uuid() id!: string;  // UUID primary key, auto-generated when omitted
  @text() title!: string;
  @text() content!: string;
}

支持的数据类型

使用这些修饰器定义字段类型:

装饰者 类型 说明
@uuid() 字符串 唯一标识符字段。
@text() 字符串 具有可选长度约束的文本字段。
@int() number 整数字段。
@decimal() number 十进制或数字字段。
@boolean() 布尔 True 或 false 字段。
@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';
}

类型修饰符

向字段修饰器添加修饰符以配置验证和约束:

修饰符 说明
{ optional: true } 允许 NULL 值。 字段默认为必填项。
{ unique: true } 添加唯一约束。
{ default: value } 设置默认值表达式。
{ max: n }{ min: n } 字符串长度约束(最大字符数和最小字符数)。
{ min: n }{ max: n } 数值约束。

注释

TypeScript 可选标记(? 属性名称之后)仅影响静态 TypeScript 类型。 它不会将数据库列设为可为 NULL。 若要将字段设为可为 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应用在定义关系时自动生成外键列。

  • 一对多 – 在父项上使用 @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 应用不支持指向系统实体(例如内置的 USER 实体)的 @one() 关系。 若要将数据行与已登录用户相关联,请添加一个普通的 user_id 类型字段,并用认证声明中的值填充该字段(通常为 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 代理:

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 应用会根据导航修饰器自动生成它们。
  • 在实体文件中使用带有 .js 扩展名的相对导入,以便生成的 ESM JavaScript 能够被正确解析。
  • 有关授权模式和基于角色的访问,请参阅 “定义数据权限”。

Troubleshooting

缺失的关系

如果 API 中未显示关系,请验证:

  • 导航修饰器(@one()@many())存在。
  • 该实体注册于rayfin/data/schema.ts