Leggere e scrivere dati con GraphQL nelle app Fabric

Fabric Apps offre un client GraphQL tipizzato in modo sicuro che consente di eseguire operazioni di creazione, lettura, aggiornamento ed eliminazione senza scrivere query grezze. Il client genera automaticamente GraphQL dalle chiamate al metodo e restituisce entità tipate in base alle definizioni del modello di dati.

Prerequisiti

  • Progetto app di Fabric con modelli di dati definiti. Vedere Definire i modelli di dati.
  • Servizi back-end in esecuzione in locale o distribuiti in Fabric.

Inizializzare il client

Crea un'istanza di RayfinClient con l'URL del backend, la chiave pubblicabile e il tipo di schema:

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

L'argomento di tipo generico consente a TypeScript di fornire il completamento automatico e il controllo del tipo per tutte le operazioni sui dati.

Leggi i dati

Accedere alle raccolte di entità tramite client.data.<EntityName>. L'API Fluent fornisce metodi per eseguire query, filtrare, ordinare e impaginazione.

Recuperare tutti i record

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

Recupera un singolo record per chiave primaria

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

Restituisce l'entità completa o null se non esiste alcun record con tale ID.

Filtrare i record

Utilizza il metodo where() per filtrare i risultati:

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

Operatori di filtro

Operatore Description Example
eq Uguale a { status: { eq: 'active' } }
ne Non uguale a { status: { ne: 'archived' } }
gt Maggiore di { age: { gt: 18 } }
gte Maggiore di o uguale a { age: { gte: 21 } }
lt Minore di { price: { lt: 100 } }
lte Minore o uguale a { price: { lte: 50 } }
contains Contiene la sottostringa { title: { contains: 'draft' } }

Ordinare i risultati

Usare orderBy() per ordinare i risultati delle query:

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

Ordinare in base a più colonne:

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

Quando si definiscono le relazioni con @one() e @many() decorator, è possibile includere i campi di entità correlati nella stessa query:

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

Ogni nota include i dati del relativo notebook senza la necessità di eseguire una query separata.

Impaginare set di risultati di grandi dimensioni

Usare la paginazione basata su cursore per elenchi di grandi dimensioni:

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

Recuperare la pagina successiva usando il cursore:

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

Note

La totalCount proprietà viene visualizzata nel PagedResult tipo ma non viene popolata dal back-end. Usare items.length per contare i risultati nella pagina corrente.

Creazione di record

Usare il create() metodo per inserire nuovi record:

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

Il metodo restituisce l'entità creata con tutti i campi popolati, incluso l'oggetto generato automaticamente id.

Creare record con relazioni

Quando si creano entità con relazioni, passare l'oggetto correlato completo o un oggetto con solo la chiave primaria:

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

Entrambe le forme producono lo stesso risultato. Usare il primo modulo quando si conosce già l'ID dell'entità correlata e si vuole evitare un recupero aggiuntivo.

Aggiorna record

Utilizzare il update() metodo per modificare i record esistenti. Passare un oggetto filtro e un oggetto contenente i campi da aggiornare:

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

Aggiornare le relazioni

Per modificare una relazione, passare la nuova entità correlata o semplicemente il relativo ID:

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

Eliminazione di record

Usare il delete() metodo per rimuovere i record corrispondenti a un filtro:

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

Il metodo viene risolto quando il back-end conferma l'eliminazione. Se nessun record corrisponde al filtro, il metodo ha comunque esito positivo.

Gestire l'autenticazione

Quando l'autenticazione è abilitata, accedere prima di eseguire operazioni di dati:

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

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

Il client collega automaticamente la sessione di autenticazione a tutte le chiamate API dati. Non è necessario passare manualmente i token.

Procedure consigliate

  • Selezionare solo i campi necessari : recuperare solo i campi usati per ridurre le dimensioni del payload e migliorare le prestazioni.
  • Usare la paginazione per elenchi di grandi dimensioni : evitare di recuperare migliaia di record contemporaneamente usando first() e executePaginated().
  • Query di relazione batch : includere campi di entità correlati nella stessa query anziché effettuare richieste separate.
  • Memorizzare nella cache i dati a cui si accede di frequente : archiviare i dati di riferimento statici in memoria per ridurre le chiamate API.

Limitazioni correnti

  • Il count() metodo non è disponibile nel client Fluent. Selezionare i campi minimi e usare results.length invece.
  • Le relazioni molti-a-molti non sono supportate. Usare un'entità di join esplicita con due @one() decoratori di navigazione.
  • La totalCount proprietà in PagedResult non viene popolata dal back-end.