Soft Deletes & Recovery

Implement soft delete, recovery, bulk operations, and permanent deletion patterns

Enable Soft Delete

Add the deletedAt column with softDelete flag:

models/Post.ts
import { defineModel, DataTypes } from "stabilize-orm";
export const Post = defineModel({
tableName: "posts",
timestamps: { createdAt: "createdAt", updatedAt: "updatedAt" },
columns: {
id: { type: DataTypes.STRING, required: true, unique: true },
title: { type: DataTypes.STRING, length: 200, required: true },
body: { type: DataTypes.TEXT },
published: { type: DataTypes.BOOLEAN, defaultValue: false },
deletedAt: { type: DataTypes.DATETIME, softDelete: true },
},
});

Soft Delete a Record

examples/soft-delete.ts
const postRepo = orm.getRepository(Post);
// Create a post
const post = await postRepo.create({
id: generateUUID(),
title: "My Post",
body: "Post content here",
});
console.log("Created:", post.id);
// Soft delete - sets deletedAt timestamp
await postRepo.delete(post.id);
console.log("Soft-deleted");
// The post is no longer returned by normal queries
const activePosts = await postRepo.find().execute(orm.client);
console.log("Active posts:", activePosts.length); // post is excluded

Find Deleted Records

examples/find-deleted.ts
// Find only soft-deleted records
const deletedPosts = await postRepo.findDeleted().execute(orm.client);
console.log("Deleted posts:", deletedPosts.length);
// Count deleted records
const deletedCount = await postRepo.countDeleted();
console.log("Deleted count:", deletedCount);

Recover Deleted Records

examples/recover.ts
// Recover a single record
const recovered = await postRepo.recover(post.id);
console.log("Recovered:", recovered.title);
// Recover all soft-deleted records
const recoveredCount = await postRepo.recoverAll();
console.log("Recovered", recoveredCount, "records");

Bulk Soft Delete

examples/bulk-delete.ts
// Bulk soft delete multiple records
await postRepo.bulkDelete([post1.id, post2.id, post3.id]);
console.log("Bulk deleted 3 posts");
// Bulk delete is also transactional and runs hooks
// beforeDelete and afterDelete hooks fire for each record

Scopes with Soft Delete

examples/scopes-soft-delete.ts
// The find() method automatically excludes soft-deleted records
const active = await postRepo.find().execute(orm.client);
// WHERE deletedAt IS NULL is automatically added
// Use findDeleted() to get only deleted records
const deleted = await postRepo.findDeleted().execute(orm.client);
// Other query methods also respect soft delete
const count = await postRepo.count(); // excludes deleted
const exists = await postRepo.exists({ title: "My Post" }); // excludes deleted
const page = await postRepo.paginate(1, 10); // excludes deleted