Seeding data
Populate @anonymous() Rayfin entities with RayfinServerClient in a Node.js script — @authenticated() entities have no scripted seeding path today.
Use RayfinServerClient from a plain Node.js script to populate development data — the
same client.data.<Entity> calls you use from your frontend, just run with tsx instead
of in a browser. A script has no browser, so it can only authenticate with the
publishableKey your project already generates — enough to satisfy an @anonymous()
grant, and nothing else. See
No supported path for @authenticated() entities
below if the entity you want to seed requires a signed-in user.
What a seed script can authenticate as
RayfinServerClient requires publishableKey; accessToken is optional:
new RayfinServerClient<AppSchema>({
baseUrl: env['RAYFIN_PUBLIC_API_URL'],
publishableKey: env['RAYFIN_PUBLIC_PUBLISHABLE_KEY'],
});Use RayfinServerClient, not RayfinClient — it skips the browser-coupled Auth module
entirely. See
@microsoft/rayfin-client.
Without an accessToken, every request carries only the publishable key. That satisfies an
@anonymous() grant and nothing else — it cannot satisfy @authenticated(), which
requires a valid signed-in user's access token on every request (see
Built-in roles).
There is no supported way for a Node.js script to acquire that token itself. Fabric SSO
signs a user in through a browser popup or the Fabric portal iframe (see
Fabric SSO) — flows a headless script cannot drive — and, per
Sessions, Rayfin doesn't expose a way to read a token
back out for reuse elsewhere. accessToken on RayfinServerClient exists for code that
already holds a token issued by some other trusted flow — for example, a Rayfin function
forwarding the caller's own request token — not as a way to sign a script in.
Service principal credentials (rayfin login --service-principal) don't help here either
— they authenticate the CLI itself for deployment (rayfin up, rayfin up db apply), a
separate concern from a signed-in user's access token that client.data.* calls check.
Seeding an anonymous entity
Reference or lookup data with no owner — content anyone can read, that only you create — is the case a seed script handles cleanly, because it needs no caller identity at all:
import { entity, anonymous, uuid, text } from '@microsoft/rayfin-core';
@entity()
@anonymous('*')
export class FaqCategory {
@uuid() id!: string;
@text({ max: 100 }) name!: string;
}import { entity, anonymous, uuid, text, one } from '@microsoft/rayfin-core';
import { FaqCategory } from './FaqCategory.js';
@entity()
@anonymous('*')
export class FaqEntry {
@uuid() id!: string;
@text({ max: 200 }) question!: string;
@text({ max: 2000 }) answer!: string;
@uuid() category_id!: string;
@one(() => FaqCategory) category!: FaqCategory;
}Note
A tenant admin has to enable "Enable anonymous data access for Fabric Apps" before
Fabric grants the anonymous role at all — see
Built-in roles. Without it, even a correctly
decorated @anonymous() entity rejects anonymous requests.
Read the backend URL and publishable key from rayfin/.env (generated by rayfin up)
rather than hardcoding them — the port and key vary per project and per deployment:
import { readFileSync } from 'fs';
import { RayfinServerClient } from '@microsoft/rayfin-client';
import type { AppSchema } from '../rayfin/data/schema';
function loadEnv(): Record<string, string> {
const vars: Record<string, string> = {};
for (const line of readFileSync('rayfin/.env', 'utf-8').split('\n')) {
const match = line.match(/^([^#=]+)=(.+)$/);
if (match) vars[match[1].trim()] = match[2].trim();
}
return vars;
}
const env = loadEnv();
const client = new RayfinServerClient<AppSchema>({
baseUrl: env['RAYFIN_PUBLIC_API_URL'],
publishableKey: env['RAYFIN_PUBLIC_PUBLISHABLE_KEY'],
});
async function seed() {
// Create parent records first, then children that reference their IDs.
const category = await client.data.FaqCategory.create({ name: 'Billing' });
await client.data.FaqEntry.create({
question: 'How do I change my plan?',
answer: 'Open Settings → Billing and choose a new plan.',
category: { id: category.id },
});
}
seed().catch(console.error);Run it with tsx:
npx tsx scripts/seed.tsOr wire it into package.json for a shorter command:
{
"scripts": {
"seed": "tsx scripts/seed.ts"
}
}npm run seedCreate parents before children. A @one() relationship needs the parent's id to
exist first — create the FaqCategory before the FaqEntry that references it, as shown
above. See Relationships for how @one() / @many() work.
Seeding a single entity
For an entity with no relationships, the script is shorter — just create:
import { readFileSync } from 'fs';
import { RayfinServerClient } from '@microsoft/rayfin-client';
import type { AppSchema } from '../rayfin/data/schema';
function loadEnv(): Record<string, string> {
const vars: Record<string, string> = {};
for (const line of readFileSync('rayfin/.env', 'utf-8').split('\n')) {
const match = line.match(/^([^#=]+)=(.+)$/);
if (match) vars[match[1].trim()] = match[2].trim();
}
return vars;
}
const env = loadEnv();
const client = new RayfinServerClient<AppSchema>({
baseUrl: env['RAYFIN_PUBLIC_API_URL'],
publishableKey: env['RAYFIN_PUBLIC_PUBLISHABLE_KEY'],
});
async function seed() {
const names = ['Billing', 'Account', 'Troubleshooting'];
for (const name of names) {
await client.data.FaqCategory.create({ name });
}
}
seed().catch(console.error);No supported path for @authenticated() entities
Most application data is not anonymous — anything with a user_id/owner_id and a
row-level policy, like the Todo entity in Build a todo app, is
scoped with @authenticated(). As established above, a seed script cannot obtain a token
for any user, so there is no supported way today to script seed data into an entity that
requires authentication.
If you need rows in an entity like that for development or testing, create them the way a
real signed-in user would — through the running app itself, signed in with Fabric SSO —
rather than through a script. Do not hand-roll an Authorization header or fabricate a
token shaped like a Fabric-issued JWT: there is no supported way to mint a valid one outside
Fabric SSO, and Rayfin rejects anything that doesn't verify against its own signing keys.
In my Rayfin project, I have an FaqCategory entity (id, name) and an FaqEntry entity (id,
question, answer, category_id, with a @one(() => FaqCategory) relationship named
category), both decorated @anonymous('*') as public reference data with no owner. Write a
Node.js seed script at scripts/seed.ts using RayfinServerClient from
@microsoft/rayfin-client. Read RAYFIN_PUBLIC_API_URL and RAYFIN_PUBLIC_PUBLISHABLE_KEY from
rayfin/.env — do not pass an accessToken, since this client only needs to satisfy the
anonymous role. Create one FaqCategory named "Billing", then one FaqEntry in that category.
Add a "seed" script to package.json that runs it with tsx.