Plugins
Soft delete
Turn delete() into a recoverable state change, filter deleted rows by default, and add restore helpers.
@better-drizzle/soft-delete extends the built-in methods with typed soft-delete controls, default visibility filtering, and restore helpers.
Install
npm install @better-drizzle/soft-deletepnpm add @better-drizzle/soft-deleteyarn add @better-drizzle/soft-deletebun add @better-drizzle/soft-deleteUsage
import { better } from 'better-drizzle';
import { softDelete } from '@better-drizzle/soft-delete';
const client = better(db, {
schema,
plugins: [
softDelete({
column: 'deletedAt',
deletedByColumn: 'deletedById',
defaults: {
mode: 'soft',
visibility: 'without',
},
}),
],
});Every option is optional. Defaults:
column: 'deletedAt'deletedByColumn: 'deletedById'defaults.mode: 'soft'defaults.visibility: 'without'
Reads exclude deleted rows by default
findMany, findFirst, count, and exists filter out soft-deleted rows automatically:
await client.users.findMany(); // visible rows onlyOpt in per query with the typed deleted arg:
await client.users.findMany({ deleted: 'with' }); // include deleted
await client.users.findMany({ deleted: 'only' }); // only deleted
await client.users.count({ deleted: 'with' });Deleting and restoring
// Soft delete (default)
await client.users.delete({ where: { id: 1 } });
// Record who deleted it (when the column exists)
await client.users.delete({ where: { id: 1 }, deletedBy: 'admin_42' });
// Force a real, physical delete
await client.users.delete({ where: { id: 1 }, mode: 'hard' });
// Restore
await client.users.restore({ where: { id: 1 } });
await client.users.restoreById(1);Behavior details
- Models without the configured column are ignored automatically.
deletedByis written only when the configured column exists on the model.restore()andrestoreById()bypass plugin transforms so they can always clear the soft-delete fields directly.mode: 'hard'falls back to the built-in physical delete.
Need a one-off hard delete elsewhere?
Any delegate can bypass plugins for a single call with
$withoutPlugins() — handy
for repair scripts that must delete beneath the soft-delete layer.
Where it fits best
- audit-sensitive systems
- admin tools where deleted rows must remain inspectable
- apps where "delete" should default to reversible