Powersync composite primary key seed
Skill kjuhwa/skills-hub/skills/powersync/powersync-composite-primary-key-seed
Design default-data tables (settings, models, modes, tasks) so each user can hold the same default IDs by using composite primary keys (id, user_id) or (key, user_id) on the backend and reconciling with a default-hash column on the frontend.From its SKILL.md
npx -y skills add kjuhwa/skills-hub --skill powersync-composite-primary-key-seedAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
3.3 KB, 631 tokens by cl100k_base, as published. Nobody here has run it
When to use
You have tables seeded at app initialization with shared default IDs (e.g. openai-gpt-4o, theme, inbox). In a PowerSync world, two users can both legitimately own a row with the same default ID, so a single-column primary key on the backend collides.
Steps
- On the backend (Postgres), declare the table with a composite primary key. For id-based tables use
(id, user_id), for settings-style tables use(key, user_id). - Update
powersyncConflictTargetinbackend/src/db/powersync-schema.tsto include both columns soINSERT ... ON CONFLICT (id, user_id)/ON CONFLICT (key, user_id)upserts per user. - Update
powersyncPkColumnfor the table so PATCH/DELETE use the "business" id column (notuser_id) in theirWHEREclauses. Always AND the clause withuser_idfrom the JWT so users can only mutate their own rows. - Do not add
.references()to this table from other tables. Use a plain column likemodeId: text('mode_id'). PowerSync treats the server as a sync target, not a query engine, so FKs add overhead and block valid partial-sync states. - On the frontend (SQLite), keep a single-column PK (
idorkey). Local data is already scoped to one user. - Add a
default_hashcolumn to detect user modifications. At startup,reconcileDefaultsForTable()inserts new defaults, back-fillsdefault_hashon rows that lack it, and updates only when the previous default hash matches the stored hash (i.e. the user has not edited). - Guard against wiping user-set values with null defaults: if
existing.value !== nullanddefaultItem.value === null, skip the update. This protects fields like locale/units that users set via other code paths.
Counter / Caveats
- Hash equality is the only modification signal. Any column not fed into the hash function effectively becomes unmanageable by
reconcileDefaults; include every field you want to keep in sync. - The approach assumes an idempotent
uuidv7()seed per device for an anonymous-id setting. If you add other per-user unique seeds, create them in the same transaction as reconcile so they survive the reset-and-reseed flow atomically. - Do not rely on composite PKs on the frontend — it confuses Drizzle tooling and PowerSync upload builds the
WHEREclause from a single business key plus user_id on the server.
Evidence
docs/composite-primary-keys-and-default-data.md:7-53src/lib/reconcile-defaults.ts:22-101— the reconcile implementation with hash guardsrc/db/tables.ts/backend/src/db/powersync-schema.ts— composite PK declarations
What ships with it: 1 file
2.3 KB alongside SKILL.md
- content.md2.3 KB