Membaca dan menulis data dengan GraphQL di Fabric Apps

Fabric Apps menyediakan klien GraphQL yang aman terhadap tipe, yang memungkinkan Anda melakukan operasi membuat, membaca, memperbarui, dan menghapus tanpa menulis kueri secara manual. Klien menghasilkan GraphQL secara otomatis dari panggilan metode Anda dan mengembalikan entitas yang diketik berdasarkan definisi model data Anda.

Prasyarat

  • Proyek Fabric Apps dengan model data yang ditentukan. Lihat Menentukan model data.
  • Layanan backend yang berjalan secara lokal atau disebarkan ke Fabric.

Menginisialisasi klien

Instansiasikan RayfinClient dengan URL backend Anda, kunci publik, dan tipe skema:

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',
});

Argumen jenis generik memungkinkan TypeScript menyediakan pelengkapan otomatis dan pemeriksaan jenis untuk semua operasi data.

Membaca data

Akses koleksi entitas melalui client.data.<EntityName>. API fasih menyediakan metode untuk mengkueri, memfilter, mengurutkan, dan penomoran halaman.

Mengambil semua rekaman

const notes = await client.data.Note.select([
  'id',
  'title',
  'content',
  'createdAt',
  'isPinned',
]).execute();

Ambil satu data berdasarkan kunci primer

const note = await client.data.Note.findByPk('00000000-0000-0000-0000-000000000000');

Ini mengembalikan entitas lengkap atau null jika tidak ada rekaman dengan ID tersebut.

Filter catatan

where() Gunakan metode untuk memfilter hasil:

const pinnedNotes = await client.data.Note.select([
  'id',
  'title',
  'isPinned',
])
  .where({ isPinned: { eq: true } })
  .execute();

Operator penyaring

Operator Deskripsi Example
eq Sama dengan { status: { eq: 'active' } }
ne Tidak sama { status: { ne: 'archived' } }
gt Lebih besar dari { age: { gt: 18 } }
gte Lebih besar dari atau sama dengan { age: { gte: 21 } }
lt Kurang dari { price: { lt: 100 } }
lte Kurang dari atau sama dengan { price: { lte: 50 } }
contains Mengandung substring { title: { contains: 'draft' } }

Urutkan hasil

Gunakan orderBy() untuk mengurutkan hasil kueri:

const notes = await client.data.Note.select([
  'id',
  'title',
  'createdAt',
])
  .orderBy({ createdAt: 'desc' })
  .execute();

Urutkan menurut beberapa kolom:

const notes = await client.data.Note.select([
  'id',
  'title',
  'isPinned',
  'createdAt',
])
  .orderBy({ isPinned: 'desc' })
  .orderBy({ createdAt: 'desc' })
  .execute();

Saat Anda mendefinisikan relasi dengan dekorator @one() dan @many(), Anda dapat menyertakan bidang entitas terkait dalam kueri yang sama:

const notes = await client.data.Note.select([
  'id',
  'title',
  'content',
  'notebook.id',
  'notebook.name',
  'notebook.color',
])
  .execute();

Setiap catatan menyertakan data notebook terkait tanpa memerlukan kueri terpisah.

Buat paginasi untuk kumpulan hasil yang besar

Gunakan penomoran halaman berbasis kursor untuk daftar besar:

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);

Ambil halaman berikutnya menggunakan kursor:

if (page.hasNextPage) {
  const nextPage = await client.data.Note.select([
    'id',
    'title',
    'createdAt',
  ])
    .orderBy({ createdAt: 'desc' })
    .first(25)
    .after(page.endCursor)
    .executePaginated();
}

Nota

Properti totalCount terdapat pada tipe PagedResult, tetapi tidak diisi oleh backend. Gunakan items.length untuk menghitung hasil di halaman saat ini.

Membuat rekaman

create() Gunakan metode untuk menyisipkan rekaman baru:

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',
});

Metode mengembalikan entitas yang dibuat dengan semua bidang terisi, termasuk id yang dibuat secara otomatis.

Membuat catatan dengan relasi

Saat membuat entitas yang memiliki hubungan, berikan objek terkait penuh atau objek hanya dengan kunci primer:

// 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(),
});

Kedua bentuk menghasilkan hasil yang sama. Gunakan bentuk pertama ketika Anda sudah mengetahui ID entitas terkait dan ingin menghindari pengambilan data tambahan.

Memperbarui rekaman

Gunakan metode update() untuk mengubah record yang sudah ada. Berikan objek filter dan objek yang berisi bidang-bidang yang akan diperbarui:

await client.data.Note.update(
  { id: 'note-123' },
  {
    title: 'Updated title',
    updatedAt: new Date(),
  }
);

Perbarui hubungan

Untuk mengubah hubungan, berikan entitas terkait baru atau hanya ID-nya:

// Move a note to a different notebook
await client.data.Note.update(
  { id: 'note-123' },
  { notebook: { id: 'new-notebook-789' } }
);

Hapus rekaman

delete() Gunakan metode untuk menghapus rekaman yang cocok dengan filter:

await client.data.Note.delete({ id: 'note-123' });

Metode ini diselesaikan ketika backend mengonfirmasi penghapusan. Jika tidak ada rekaman yang cocok dengan filter, metode masih berhasil.

Menangani autentikasi

Saat autentikasi diaktifkan, masuk sebelum melakukan operasi data:

await client.auth.signIn({ email, password });

// All subsequent data calls include authentication context
const notes = await client.data.Note.select(['id', 'title']).execute();

Klien secara otomatis melampirkan sesi autentikasi ke semua panggilan API data. Anda tidak perlu mengirimkan token secara manual.

Praktik terbaik

  • Pilih hanya bidang yang diperlukan – Ambil hanya bidang yang Anda gunakan untuk mengurangi ukuran payload dan meningkatkan performa.
  • Gunakan penomoran halaman untuk daftar besar – Hindari mengambil ribuan rekaman sekaligus dengan menggunakan first() dan executePaginated().
  • Kueri hubungan batch – Sertakan bidang entitas terkait dalam kueri yang sama daripada membuat permintaan terpisah.
  • Cache data yang sering diakses – Menyimpan data referensi statis dalam memori untuk mengurangi panggilan API.

Keterbatasan saat ini

  • Metode count() ini tidak tersedia pada klien yang fasih. Pilih bidang minimal dan gunakan results.length sebagai gantinya.
  • Relasi banyak-ke-banyak tidak didukung. Gunakan entitas penghubung eksplisit dengan dua dekorator navigasi @one().
  • Properti totalCount pada PagedResult tidak diisi oleh backend.