Developer

Entities & Storage

Typed per-resource SQLite with defineEntity - schema, queries, indexes, and reactive subscriptions.

Entities are typed storage. By default each entity is project-tier SQLite: one database per resource at {projectFolder}/{resource folderPath}/{filename} (default data.db). The database syncs with the project folder, so multiple applet resources live side by side without colliding.

defineEntity

import { defineEntity, z } from "@rightplace/applet-sdk/v2";

export const Feed = defineEntity({
  name: "feed",
  storage: "sqlite",                  // project-tier (default)
  filename: "feed.db",                // optional; default "data.db"
  schema: z.object({
    id: z.string().uuid(),
    url: z.string().url(),
    title: z.string(),
    createdAt: z.number().int(),
  }),
  primaryKey: "id",
  indexes: [{ on: ["url"], unique: true }],
});

CRUD

await Feed.insert({ id: ctx.uuid(), url, title, createdAt: ctx.now() });
await Feed.update(id, { title });
await Feed.delete(id);

const one = await Feed.findById(id);
const list = await Feed.find({
  where: {
    isMuted: false,
    pubDate: { $gt: weekAgo, $lt: now },
    title: { $like: "%react%" },
    feedId: { $in: [a, b, c] },
  },
  orderBy: { pubDate: "desc" },
  limit: 50,
  offset: 0,
});
const cnt = await Feed.count({ where: {} });

Where-clause operators

OperatorMeaningSQL
$eq (default)equalitycol = ?
$nenot equalcol <> ?
$lt / $lteless than (or equal)col < ? / col <= ?
$gt / $gtegreater than (or equal)col > ? / col >= ?
$in / $ninmembershipcol IN (...) / col NOT IN (...)
$likecase-sensitive patterncol LIKE ?
$ilikecase-insensitive patternLOWER(col) LIKE LOWER(?)
$nullboolean for IS NULLcol IS NULL / col IS NOT NULL

Raw escape hatch

For queries the operators cannot express, drop to Kysely:

const rows = await Feed.raw((qb) =>
  qb.selectFrom("feed").innerJoin("article", "article.feed_id", "feed.id").execute(),
);

Reactive subscriptions

Every write auto-emits entity:{name}:inserted|updated|deleted. Subscriptions are driven by those events:

const sub = Feed.subscribe((list) => updateUI(list), { where: { isMuted: false } });
sub.unsubscribe();

Storage backends

BackendTierWhen
sqlite (default)project-tierper-resource data; syncs with project
jsonapp-datasmall config-shape data, under ~100 rows
app-data-sqliteapp-datalegacy singleton applets with no resources

Migrations

Schema changes are snapshotted under schema-history/{entity}-vN.json with a matching {entity}.migrations.ts. The packager rejects a schema bump without a matching migration. For project-tier entities, migrations run per resource on lifecycle (onResourceCreate).