Leer y escribir datos con GraphQL en Fabric Apps

Fabric Apps proporciona un cliente GraphQL con seguridad de tipos que permite realizar operaciones de creación, lectura, actualización y eliminación sin tener que escribir consultas directamente. El cliente genera GraphQL automáticamente a partir de las llamadas a tus métodos y devuelve entidades tipadas según las definiciones de tu modelo de datos.

Prerequisites

  • Un proyecto de Fabric Apps con modelos de datos definidos. Consulte Definición de modelos de datos.
  • Los servicios back-end que se ejecutan localmente o se implementan en Fabric.

Inicialización del cliente

Cree RayfinClient una instancia con la dirección URL de back-end, la clave que se puede publicar y el 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',
});

El argumento de tipo genérico permite a TypeScript ofrecer autocompletado y verificación de tipos en todas las operaciones de datos.

Lectura de datos

Acceda a colecciones de entidades a través de client.data.<EntityName>. La API fluida proporciona métodos para consultar, filtrar, ordenar y paginar.

Capturar todos los registros

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

Captura de un único registro por clave principal

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

Esto devuelve la entidad completa o null si no existe ningún registro con ese identificador.

Filtrar registros

Use el where() método para filtrar los resultados:

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

Operadores de filtro

Operador Descripción Ejemplo
eq Es igual { status: { eq: 'active' } }
ne No es igual { status: { ne: 'archived' } }
gt Mayor que { age: { gt: 18 } }
gte Mayor o igual que { age: { gte: 21 } }
lt Menor que { price: { lt: 100 } }
lte Menor o igual que { price: { lte: 50 } }
contains Contiene subcadena { title: { contains: 'draft' } }

Ordenar resultados

Use orderBy() para ordenar los resultados de la consulta:

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

Ordenar por varias columnas:

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

Al definir relaciones con @one() y @many() decoradores, puede incluir campos de entidad relacionados en la misma consulta:

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

Cada nota incluye sus datos de cuaderno asociados sin necesidad de una consulta independiente.

Paginar grandes conjuntos de resultados

Use la paginación basada en cursores 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);

Capture la página siguiente con el cursor:

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

Note

La propiedad totalCount aparece en el tipo PagedResult, pero el backend no la rellena. Use items.length para contar los resultados en la página actual.

Crear registros

Use el create() método para insertar nuevos 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',
});

El método devuelve la entidad creada con todos los campos rellenados, incluido el generado automáticamente id.

Creación de registros con relaciones

Al crear entidades que tienen relaciones, pase el objeto relacionado completo o un objeto con solo la clave principal:

// 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 formas generan el mismo resultado. Use el primer formulario cuando ya conozca el identificador de la entidad relacionada y quiera evitar una captura adicional.

Actualizar registros

Use el update() método para modificar los registros existentes. Pase un objeto de filtro y un objeto que contenga los campos que se van a actualizar:

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

Actualización de relaciones

Para cambiar una relación, pase la nueva entidad relacionada o simplemente su identificador:

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

Eliminar registros

Use el delete() método para quitar registros que coincidan con un filtro:

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

El método se resuelve cuando el back-end confirma la eliminación. Si no hay registros que coincidan con el filtro, el método sigue funcionando correctamente.

Gestionar la autenticación

Cuando la autenticación está habilitada, inicie sesión antes de realizar operaciones de datos:

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

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

El cliente adjunta automáticamente la sesión de autenticación a todas las llamadas api de datos. No es necesario pasar tokens manualmente.

procedimientos recomendados

  • Seleccionar solo los campos necesarios : capture solo los campos que use para reducir el tamaño de la carga y mejorar el rendimiento.
  • Usar paginación para listas grandes : evite capturar miles de registros a la vez mediante first() y executePaginated().
  • Consultas de relaciones por lotes – Incluya los campos de entidades relacionadas en la misma consulta en lugar de realizar solicitudes separadas.
  • Almacenar datos a los que se accede con frecuencia : almacene datos de referencia estáticos en memoria para reducir las llamadas API.

Limitaciones actuales

  • El count() método no está disponible en el cliente fluent. Seleccione los campos mínimos y use results.length en su lugar.
  • No se admiten relaciones de varios a varios. Use una entidad de combinación explícita con dos @one() decoradores de navegación.
  • La propiedad totalCount de PagedResult no la completa el backend.