Store Functions And Transactions
Every store in server/**/store.ts exposes full CRUD for its entity: create,
get + list, update, delete. Naming follows three rules:
- Unsuffixed means "by ids."
deleteCustomFieldValues({ ids })deletes by value id. Any other selector is explicit:deleteCustomFieldValuesByTaskIds,deleteCustomFieldValuesByFieldIds,updateCustomFieldValuesByTaskId. - The plural is the implementation; the singular delegates to it with a
one-element id list.
deleteTaskcallsdeleteTasks,updateCustomFieldValuecallsupdateCustomFieldValues. Never write the same query twice. listtakes every selector as optional rather than splitting intoByXvariants —listStoredItems({ ids?, includeDone? }),listCustomFieldValues({ ids?, taskIds?, fieldIds? }).
Where a patch is genuinely per-row (custom field title/config, a task's field
values), the bulk form takes one entry per id instead of one patch across ids.
Upsert counts as create; do not add a separate create for upserted rows.
Action names are a separate public surface and do not follow this convention:
the bulk-delete-tasks action still exists and calls deleteTasks.
Every function takes the database handle as an optional trailing argument,
so it runs standalone or joins a caller's transaction. The handle is defined
once, in server/db/transaction.ts:
export type DbHandle = Pick<
ReturnType<typeof getDb>,
"select" | "insert" | "update" | "delete" | "transaction"
>;
Related Skills
- actions — actions are the callers that compose these store functions.
- task-inbox-workflow — the task/inbox operations backed by these stores.