Ler e gravar dados com o GraphQL em aplicativos Fabric

Fabric Apps fornece um cliente GraphQL com segurança de tipo que permite executar operações de criação, leitura, atualização e exclusão sem gravar consultas brutas. O cliente gera o GraphQL automaticamente de suas chamadas de método e retorna entidades tipadas com base em suas definições de modelo de dados.

Pré-requisitos

  • Um projeto Fabric Apps com modelos de dados definidos. Consulte Definir modelos de dados.
  • Os serviços de backend em execução localmente ou implantados no Fabric.

Inicializar o cliente

Crie uma instância RayfinClient com a URL de back-end, a chave publicável e o tipo de esquema:

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

O argumento de tipo genérico permite que o TypeScript forneça preenchimento automático e verificação de tipo para todas as operações de dados.

Ler dados

Acesse coleções de entidades por meio de client.data.<EntityName>. A API fluente fornece métodos para consulta, filtragem, classificação e paginação.

Buscar todos os registros

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

Buscar um único registro por chave primária

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

Isso retorna a entidade completa ou null se não houver nenhum registro com essa ID.

Filtrar registros

Use o where() método para filtrar os resultados:

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

Operadores de filtro

Operador Description Exemplo
eq Igual { status: { eq: 'active' } }
ne Não é igual a { status: { ne: 'archived' } }
gt Maior que { age: { gt: 18 } }
gte Maior ou igual { age: { gte: 21 } }
lt Menor que { price: { lt: 100 } }
lte Inferior ou igual { price: { lte: 50 } }
contains Contém subsequência de caracteres { title: { contains: 'draft' } }

Classificar resultados

Use orderBy() para classificar os resultados da consulta:

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

Classificar por várias colunas:

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

Ao definir relações com @one() e @many() decoradores, você pode incluir campos de entidade relacionados na mesma consulta:

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

Cada anotação inclui seus dados de notebook associados sem a necessidade de uma consulta separada.

Paginar grandes conjuntos de resultados

Use a paginação baseada em cursor para listas grandes:

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

Busque a próxima página usando o cursor:

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

Note

A totalCount propriedade aparece no PagedResult tipo, mas não é preenchida pelo back-end. Use items.length para contar os resultados na página atual.

Criar registros

Use o create() método para inserir novos registros:

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

O método retorna a entidade criada com todos os campos preenchidos, incluindo o gerado automaticamente id.

Criar registros com relações

Ao criar entidades que têm relações, passe o objeto relacionado completo ou um objeto apenas com a chave primária:

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

Ambas as formas produzem o mesmo resultado. Use o primeiro formulário quando você já souber a ID da entidade relacionada e quiser evitar uma busca extra.

Atualizar registros

Use o update() método para modificar registros existentes. Passe um objeto de filtro e um objeto que contém os campos a serem atualizados:

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

Atualizar relações

Para alterar uma relação, passe a nova entidade relacionada ou apenas sua ID:

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

Excluir registros

Use o delete() método para remover registros correspondentes a um filtro:

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

O método é resolvido quando o back-end confirma a exclusão. Se nenhum registro corresponder ao filtro, o método ainda terá êxito.

Gerenciar autenticação

Quando a autenticação estiver habilitada, entre antes de executar operações de dados:

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

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

O cliente anexa automaticamente a sessão de autenticação a todas as chamadas à API de dados. Você não precisa passar tokens manualmente.

Práticas recomendadas

  • Selecione apenas os campos necessários – busque apenas os campos usados para reduzir o tamanho da carga e melhorar o desempenho.
  • Usar paginação para listas grandes – evite buscar milhares de registros ao mesmo tempo usando first() e executePaginated().
  • Consultas de relação em lote – inclua campos de entidade relacionados na mesma consulta em vez de fazer solicitações separadas.
  • Armazenar dados acessados com frequência em cache – armazene dados de referência estáticos na memória para reduzir as chamadas à API.

Limitações atuais

  • O método count() não está disponível no cliente Fluente. Selecione campos mínimos e use results.length em vez disso.
  • Não há suporte para relacionamentos muitas-para-muitas. Use uma entidade de junção explícita com dois @one() decoradores de navegação.
  • A propriedade totalCount em PagedResult não é preenchida pelo backend.