Skip to content

Automatic CUD Tracking

Automatic tracking works via Prisma $extends. When you use the extended client for business writes, create, update, delete, upsert, and batch operations are automatically tracked.

Configuration

Tracking behavior is configured through createAuditExtension(options):

OptionTypeDefaultDescription
trackedModelsstring[]all models when omittedAllowlist of Prisma model names to track. trackedModels: [] means no models are audited
ignoredModelsstring[][]Denylist used only when trackedModels is not set
sensitiveFieldsstring[][]Fields to mask as [REDACTED] in diffs
sensitiveFieldsByModelRecord<string, string[]>{}Per-model fields unioned with sensitiveFields
primaryKeyRecord<string, string>{ *: 'id' }Map of model name to primary key field name
tenantRequiredbooleanfalseSkip automatic audit rows when tenant context is missing and report the skip; the business mutation still returns
tenantResolver() => string | nullCustom tenant lookup
onAuditError(error, ctx) => voidStructured audit failure callback
logFailuresbooleanfalseRecord best-effort failure audit rows for business write errors
ignoreTimestampOnlyUpdatesbooleanfalseSuppress @updatedAt-only update entries

When neither trackedModels nor ignoredModels is configured, all Prisma models are audited. Set trackedModels explicitly to keep a narrow allowlist.

Transaction Model

PathCaller tx participationAudit insert
Automatic tracking (extension)Business write keeps caller $transactionBest-effort via the base client; automatic audit inserts do not join the caller transaction
Manual logging (log(input, tx))Yes — when tx providedParticipates in provided transaction
Manual logging (log(input))NoIndependent write via base client

The key contract is explicit: automatic audit inserts do not join the caller transaction. If the caller transaction rolls back, the business row rolls back but the automatic audit row can remain as an orphan row. For updates inside an open transaction, automatic before/after diffs are based on committed state visible to the base client, so the diff can be empty or stale.

When transaction consistency matters, use AuditService.log(input, tx) for the audit row you need to roll back with the business work. experimentalTxAudit is an opt-in experimental path with no semver guarantee.

Decorators

Apply to individual handlers or entire controllers:

typescript
@NoAudit()      // Skip audit tracking for this route or controller
@AuditAction('user.role.changed')  // Override auto-generated action name

Multi-Tenancy

Tenant resolution uses this order: explicit tenantResolver, optional @nestarc/tenancy, then null.

ScenarioBehavior
Not installedtenant_id is null, library works normally
Installed, context availabletenant_id auto-injected
Automatic tracking with tenantRequired: falseWrites an audit row with tenant_id = null
Automatic tracking with tenantRequired: trueSkips the audit row, reports audit entry skipped, and the business mutation still returns
AuditService.log() / query() with tenantRequired: trueThrows unless tenant context is available or an explicit tenant option is provided

Nested Writes

Nested relation writes are not fully audited in 0.2.0. When a tracked model mutation contains a nested write such as posts.create, the extension records the top-level model mutation and emits one logger warning per model/relation.

Released under the MIT License.