Browse topics
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
| Operator | Meaning | SQL |
|---|---|---|
$eq (default) | equality | col = ? |
$ne | not equal | col <> ? |
$lt / $lte | less than (or equal) | col < ? / col <= ? |
$gt / $gte | greater than (or equal) | col > ? / col >= ? |
$in / $nin | membership | col IN (...) / col NOT IN (...) |
$like | case-sensitive pattern | col LIKE ? |
$ilike | case-insensitive pattern | LOWER(col) LIKE LOWER(?) |
$null | boolean for IS NULL | col 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
| Backend | Tier | When |
|---|---|---|
sqlite (default) | project-tier | per-resource data; syncs with project |
json | app-data | small config-shape data, under ~100 rows |
app-data-sqlite | app-data | legacy 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).