Fabric Apps 提供了一个类型安全的 GraphQL 客户端,使你无需编写原始查询即可执行创建、读取、更新和删除操作。 客户端会根据您的方法调用自动生成 GraphQL,并基于您的数据模型定义返回类型化实体。
先决条件
- 定义数据模型的Fabric应用项目。 请参阅 “定义数据模型”。
- 在本地运行或部署到 Fabric 的后端服务。
初始化客户端
使用您的后端 URL、可公开密钥和 schema 类型来实例化 RayfinClient:
import { RayfinClient } from '@microsoft/rayfin-client';
import type { Note } from '../rayfin/data/Note';
import type { Notebook } from '../rayfin/data/Notebook';
type AppSchema = {
Note: Note;
Notebook: Notebook;
};
const client = new RayfinClient<AppSchema>({
baseUrl: import.meta.env.VITE_RAYFIN_API_URL ?? 'http://localhost:5168',
publishableKey: 'pk-your-project-key',
});
泛型类型参数使 TypeScript 能够为所有数据操作提供自动完成和类型检查。
读取数据
通过 client.data.<EntityName> 访问实体集合。 Fluent API 提供了用于查询、筛选、排序和分页的方法。
获取所有记录
const notes = await client.data.Note.select([
'id',
'title',
'content',
'createdAt',
'isPinned',
]).execute();
按主键提取单个记录
const note = await client.data.Note.findByPk('00000000-0000-0000-0000-000000000000');
如果存在该 ID 对应的记录,这将返回完整的实体;否则返回 null。
筛选记录
使用where()方法筛选结果:
const pinnedNotes = await client.data.Note.select([
'id',
'title',
'isPinned',
])
.where({ isPinned: { eq: true } })
.execute();
过滤器操作符
| Operator | 说明 | 例子 |
|---|---|---|
eq |
等于 | { status: { eq: 'active' } } |
ne |
不等于 | { status: { ne: 'archived' } } |
gt |
大于 | { age: { gt: 18 } } |
gte |
大于或等于 | { age: { gte: 21 } } |
lt |
小于 | { price: { lt: 100 } } |
lte |
小于或等于 | { price: { lte: 50 } } |
contains |
包含子字符串 | { title: { contains: 'draft' } } |
对结果进行排序
用于 orderBy() 对查询结果进行排序:
const notes = await client.data.Note.select([
'id',
'title',
'createdAt',
])
.orderBy({ createdAt: 'desc' })
.execute();
按多个列排序:
const notes = await client.data.Note.select([
'id',
'title',
'isPinned',
'createdAt',
])
.orderBy({ isPinned: 'desc' })
.orderBy({ createdAt: 'desc' })
.execute();
导航关系
定义与@one()@many()修饰器的关系时,可以在同一查询中包含相关实体字段:
const notes = await client.data.Note.select([
'id',
'title',
'content',
'notebook.id',
'notebook.name',
'notebook.color',
])
.execute();
每个注释都包含其关联的笔记本数据,而无需单独的查询。
对较大的结果集进行分页
对大型列表使用基于游标的分页:
const page = await client.data.Note.select([
'id',
'title',
'createdAt',
])
.orderBy({ createdAt: 'desc' })
.first(25)
.executePaginated();
console.log('Items:', page.items);
console.log('Has next page:', page.hasNextPage);
console.log('End cursor:', page.endCursor);
使用光标提取下一页:
if (page.hasNextPage) {
const nextPage = await client.data.Note.select([
'id',
'title',
'createdAt',
])
.orderBy({ createdAt: 'desc' })
.first(25)
.after(page.endCursor)
.executePaginated();
}
注释
totalCount 属性出现在 PagedResult 类型上,但后端不会为其赋值。 使用 items.length 统计当前页面中的结果数。
创建记录
create()使用该方法插入新记录:
const newNote = await client.data.Note.create({
title: 'Meeting notes',
content: 'Discussion points from the team sync',
isPinned: false,
isArchived: false,
createdAt: new Date(),
updatedAt: new Date(),
user_id: 'user-123',
});
该方法返回已创建的实体,并填充了所有字段,包括自动生成的 id。
创建具有关联关系的记录
创建具有关联关系的实体时,传入完整的关联对象,或仅传入包含主键的对象:
// Option 1: Pass just the ID
const note = await client.data.Note.create({
title: 'Weekly summary',
content: 'Summary of this week',
notebook: { id: 'notebook-456' },
isPinned: false,
isArchived: false,
createdAt: new Date(),
updatedAt: new Date(),
});
// Option 2: Pass the full object
const notebook = await client.data.Notebook.findByPk('notebook-456');
const note = await client.data.Note.create({
title: 'Weekly summary',
content: 'Summary of this week',
notebook: notebook,
isPinned: false,
isArchived: false,
createdAt: new Date(),
updatedAt: new Date(),
});
这两种形式生成相同的结果。 当你已经知道相关实体的 ID,且想避免额外的获取时,请使用第一种形式。
更新记录
使用 update() 方法修改现有记录。 传递筛选器对象和包含要更新的字段的对象:
await client.data.Note.update(
{ id: 'note-123' },
{
title: 'Updated title',
updatedAt: new Date(),
}
);
更新关系
若要更改关系,请传递新的相关实体或其 ID:
// Move a note to a different notebook
await client.data.Note.update(
{ id: 'note-123' },
{ notebook: { id: 'new-notebook-789' } }
);
删除记录
使用此方法 delete() 删除与筛选器匹配的记录:
await client.data.Note.delete({ id: 'note-123' });
该方法会在后端确认已删除后完成。 如果没有与筛选器匹配的记录,该方法仍然成功。
处理身份验证
启用身份验证后,请在执行数据操作之前登录:
await client.auth.signIn({ email, password });
// All subsequent data calls include authentication context
const notes = await client.data.Note.select(['id', 'title']).execute();
客户端会自动将身份验证会话附加到所有数据 API 调用。 无需手动传递令牌。
最佳做法
- 仅选择所需的字段 – 仅提取用于减小有效负载大小并提高性能的字段。
-
对大型列表使用分页 – 避免使用
first()和executePaginated()一次性提取数千条记录。 - 批处理关系查询 - 在同一查询中包含相关实体字段,而不是发出单独的请求。
- 缓存经常访问的数据 - 在内存中存储静态引用数据以减少 API 调用。
当前限制
- 该方法
count()在 fluent 客户端上不可用。 选择最小字段并改用results.length。 - 不支持多对多关系。 使用一个显式联结实体,并配合两个
@one()导航修饰器。 -
PagedResult上的totalCount属性不是由后端填充的。