Running locally with sample data
backend: { provider: "mock" } lets the app run with no env — but "mock mode" doesn't mean "populated mode". Mock providers start out empty: you open the calendar and there are no bookings, you open financials and there are no entries. The "sample data" promise only pays off once you seed the provider at construction time. This page is the direct map of the seed seams, plus two details that bite anyone consuming the published packages: the dashboard in mock mode, and FAYZ_SDK_SOURCE.
This is about the second consumption path — code that imports the published @fayz-ai/* packages and assembles the app in src/ (via defineSaas / plugin factories), not the manifest-first app the platform renders. If you're in the Quickstart flow, populated mock data is the platform's responsibility; here you do it by hand.
Why mock starts out empty
A mock provider is an in-memory implementation of a plugin's data contract. It exists so the app can compile and run without a backend — not to guess what kind of store you're building. Without a seed, the list is empty by design. Seeding means passing your domain objects in when you construct the provider; from then on the mock treats them as the existing "database" (creating, editing, and filtering on top of them).
There are four seams, from the most specific (a single plugin) to the most generic (a CRUD entity).
Agenda: createMockAgendaProvider({ seed })
createMockAgendaProvider accepts a MockAgendaSeed (from @fayz-ai/plugin-agenda). You pass the seeded provider into the plugin factory via dataProvider:
import { createAgendaPlugin, createMockAgendaProvider } from '@fayz-ai/plugin-agenda'
import type { MockAgendaSeed } from '@fayz-ai/plugin-agenda'
const seed: MockAgendaSeed = {
professionals: [
{ id: 'p1', name: 'Ana', /* … */ },
],
bookings: [
{ id: 'b1', professionalId: 'p1', startsAt: '2026-07-20T14:00:00Z', /* … */ },
],
// schedules? also accepted; any collection you omit falls back to the built-in example
}
export const agenda = createAgendaPlugin({
dataProvider: createMockAgendaProvider({ seed }),
})
Shortcut: createMockAgendaProvider() with no seed already ships a ready-made example — a salon-style calendar with built-in professionals and time slots. It's the fastest way to see the calendar populated; use seed only when you want data from your own scenario (a clinic, a studio). Any collection you omit from the seed falls back to that built-in example.
Financial: createMockFinancialProvider({ seed })
Same shape for financials. MockFinancialSeed (from @fayz-ai/plugin-financial) pre-populates the ledger — invoices, movements, accounts, payment methods:
import { createFinancialPlugin, createMockFinancialProvider } from '@fayz-ai/plugin-financial'
import type { MockFinancialSeed } from '@fayz-ai/plugin-financial'
const seed: MockFinancialSeed = {
invoices: [ /* … */ ],
movements: [ /* … */ ],
// bankAccounts? falls back to two default accounts when absent; everything else stays empty if omitted
}
export const financial = createFinancialPlugin({
dataProvider: createMockFinancialProvider({ seed }),
})
Unlike the calendar, financials do not invent invoices or movements when the seed is omitted — the collections stay empty (except bankAccounts, which falls back to two default accounts). If you want the module populated, pass a seed.
Any entity: createMockProvider(entityDef, initialData)
For generic CRUD — your own plugin, your own entity — @fayz-ai/core exposes createMockProvider. The second argument is the initial array:
import { createMockProvider } from '@fayz-ai/core'
const provider = createMockProvider(clientEntityDef, [
{ id: 'c1', name: 'Example Customer', email: 'example@test.com' },
{ id: 'c2', name: 'Another Customer', email: 'another@test.com' },
])
The first argument can be the EntityDef (which is where it derives the searchable fields and the default sort from) or just an array of searchable keys. initialData defaults to [] — hence the emptiness.
A SaaS CRUD page: createCrudPage(entity, { mockData })
At the page level, createCrudPage (from @fayz-ai/saas) accepts mockData directly in its options — handy when you're assembling a list screen without an explicit provider:
import { createCrudPage } from '@fayz-ai/saas'
const ClientsPage = createCrudPage(clientEntity, {
mockData: [
{ id: 'c1', name: 'Example Customer' },
{ id: 'c2', name: 'Another Customer' },
],
})
The dashboard shows zeros in mock mode
plugin-dashboard reads Supabase directly to compose its KPIs — it doesn't go through the other plugins' mock providers. In mock mode, with no Supabase configured, the cards come back zeroed out. Seeding the calendar or the financials doesn't change that: the dashboard can't see those seeds.
The honest way out is to not rely on the built-in dashboard in mock mode, and instead register custom widgets with compute that derive from the same seeds. A KPI-type widget accepts a compute?: () => Promise<KpiValue> — you calculate the number from your own sample data:
// a widget that derives the number from the seeds instead of reading Supabase
{
id: 'custom:open-invoices',
compute: async () => ({ value: seed.invoices.filter((i) => i.status === 'open').length }),
}
That way the dashboard reflects the mock scenario instead of showing zeros. Once you switch to the real Supabase backend, the built-in dashboard starts computing for real and those custom widgets become optional.
FAYZ_SDK_SOURCE: pin the published packages
The fayzVite helper (from @fayz-ai/sdk/vite) has a convenience behavior for the monorepo: if a fayz-sdk checkout exists next to the app (at ../../fayz-sdk), it aliases the @fayz-ai/* imports to that monorepo's local source, not to node_modules. Great if you're developing the SDK; a trap for a standalone app that just wants to consume the published packages — suddenly the app is running against unpublished source that happens to be on disk.
The control for this is the FAYZ_SDK_SOURCE env var. A standalone app should pin it to published in its scripts, guaranteeing it always resolves packages from node_modules:
{
"scripts": {
"dev": "FAYZ_SDK_SOURCE=published vite",
"build": "FAYZ_SDK_SOURCE=published vite build"
}
}
With FAYZ_SDK_SOURCE=published, fayzVite ignores any neighboring checkout and uses exactly the versions in your dependencies — which is what you want when testing the app the way an external developer would.
Next: the second consumption path in Headless.