Skip to content

Entity History

Entity history records a entity of entity values on every create, update, and delete. This gives you a full audit trail with diffs.

Enable history in initializeRebaseBackend:

await initializeRebaseBackend({
// ...
history: true
});

Or with custom retention settings:

history: {
maxEntries: 200, // Per entity, oldest pruned first (default: 200)
ttlDays: 90 // Entries older than this are pruned (default: 90)
}

Mark which collections should track history:

const ordersCollection: CollectionConfig = {
slug: "orders",
history: true, // Enable for this collection
properties: { /* ... */ }
};
  1. The backend creates a rebase.entity_history table automatically.
  2. On every create, update, or delete, a entity is recorded with:
    • Entity ID, collection slug, and table name
    • The full entity values (before and after)
    • Timestamp and user ID
    • Operation type (create, update, delete)
    • An array of changed_fields showing which columns were modified

To avoid recording redundant logs where fields are saved but no values change, the HistoryService performs a structural deep equality comparison on the top-level keys of the old and new values:

  • It ignores system metadata properties starting with __.
  • If differences are found, the names of the modified properties are saved in the changed_fields (text[]) column.
  • If the deep equality check detects zero changes, the history insertion is entirely skipped.

Unlike traditional systems that rely entirely on slow periodic batch scripts, Rebase enforces your retention policies continuously:

  • Right after a entity is saved or deleted, the server schedules an inline asynchronous sweep in a non-blocking, fire-and-forget promise.
  • This sweep immediately checks retention limits for that specific entity ID and prunes older entries exceeding maxEntries or ttlDays.
GET /api/data/:slug/:entityId/history

Returns a list of history entries for a specific entity, ordered by most recent first:

{
"data": [
{
"id": 42,
"entity_id": "123",
"collection_slug": "orders",
"operation": "update",
"values": { "status": "shipped", "total": 99.99 },
"previous_values": { "status": "pending", "total": 99.99 },
"user_id": "admin-user-id",
"created_at": "2025-01-15T10:30:00Z"
}
]
}
SettingDefaultDescription
maxEntries200Maximum entries retained per individual entity ID. Oldest entries are deleted.
ttlDays90Entries older than this duration (in days) are deleted.
  1. Inline Pruning (Continuous): Executed asynchronously immediately after any CRUD operation. It prunes entries for the active entity using a sub-select query with an OFFSET equivalent to your maxEntries setting, deleting everything beyond that threshold.
  2. Global Pruning (Periodic): A background cleanup cron sweep (pruneExpired) runs every 6 hours to evaluate global ttlDays thresholds and clean up orphaned logs across all collections.