Audit trail¶
withTimestamps records when a row changed and withAudit records who touched
it last. Neither keeps the history: one entry per create, update and delete, with the
actor, the action and a before/after diff.
import { auditLogModel, enableAudit } from "tempest-db-js";
class AuditLog extends auditLogModel("audit_log") {}
enableAudit(Order, {
log: AuditLog,
actor: () => currentUser()?.id ?? null, // (1)!
exclude: ["passwordHash"], // (2)!
});
- Called at write time, so it can read request-scoped context.
- Columns not worth recording — a hash, a large blob.
What gets stored¶
| Column | Content |
|---|---|
tableName / rowKey |
which table and which primary key (an object; a composite key in full) |
action |
insert · update · delete |
actor |
whatever the resolver returned, or null |
changes |
{ column: [before, after] } — only what changed on an update; the whole row on insert/delete |
at |
when |
Written in the same transaction¶
enableAudit wires the repository signals, so the entry is written on
the same session — and therefore in the same transaction — as the change:
await session.transaction(async () => {
await orders.create(order); // row + audit entry
throw new Error("business rule");
});
// neither survives
Auditing a change that rolled back would be a lie
That is why the entry is not written "afterwards", in another transaction: a trail that records a change which never committed is worse than no trail.
An update that changes nothing writes nothing
update({ id }, { total: 10 }) on an order already worth 10 stores nothing — the
diff comes out empty. A trail full of delta-less entries hides the ones that matter.
The before side costs a SELECT
To have the "before" of the diff, the preSave handler reads the current row. Every
diffing trail pays that; if it hurts on a hot table, audit only the models that need
it.
Recap¶
auditLogModel(table)gives the schema;enableAudit(Model, ...)turns it on and returns the switch that turns it off.- The diff carries only what changed, with the actor resolved at write time.
- Same transaction as the change — a rollback takes the entry with it. 🚀