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 表,其中包含 id、title、description、createdAt 和 updatedAt 列。
主键
每个实体都使用一个名为其主键的 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
}
不支持自定义外键名称(foreignKey 和 targetKey 选项)。
参考系统实体
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;
};
每当创建新实体时更新此类型。
应用架构更改
定义或修改实体后,将更改应用于数据库:
将更新的架构部署到Fabric:
npx rayfin up db apply如果架构更改包括破坏性操作(删除列、重命名表),CLI 会警告你,并拒绝继续。 使用
--force跳过安全检查:npx rayfin up db apply --force
注释
使用 --force 可能会导致数据丢失。 在继续操作之前,请仔细查看列出的操作。
最佳做法
- 仅当你需要在代码中读取或设置外键字段时,才定义这些字段——Fabric 应用会根据导航修饰器自动生成它们。
- 在实体文件中使用带有
.js扩展名的相对导入,以便生成的 ESM JavaScript 能够被正确解析。 - 有关授权模式和基于角色的访问,请参阅 “定义数据权限”。
Troubleshooting
缺失的关系
如果 API 中未显示关系,请验证:
- 导航修饰器(
@one()或@many())存在。 - 该实体注册于
rayfin/data/schema.ts。