Fabric Apps での GraphQL を使用したデータの読み取りと書き込み

Fabric Apps には、生のクエリを記述せずに作成、読み取り、更新、および削除の操作を実行できる、タイプ セーフな GraphQL クライアントが用意されています。 クライアントは、メソッド呼び出しから GraphQL を自動的に生成し、データ モデル定義に基づいて型指定されたエンティティを返します。

前提条件

  • データ モデルが定義された Fabric Apps プロジェクト。 データ モデルの定義を参照してください。
  • ローカルで実行されているか、Fabricにデプロイされているバックエンド サービス。

クライアントを初期化する

バックエンド URL、発行可能なキー、スキーマの種類を使用して 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();
}

Note

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 を使用します。
  • 多対多リレーションシップはサポートされていません。 2 つの @one() ナビゲーション デコレーターで明示的な結合エンティティを使用します。
  • totalCountPagedResult プロパティは、バックエンドによって設定されません。