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 postconst post = await postRepo.create({ id: generateUUID(), title: "My Post", body: "Post content here",});console.log("Created:", post.id);
// Soft delete - sets deletedAt timestampawait postRepo.delete(post.id);console.log("Soft-deleted");
// The post is no longer returned by normal queriesconst activePosts = await postRepo.find().execute(orm.client);console.log("Active posts:", activePosts.length); // post is excludedFind Deleted Records
examples/find-deleted.ts
// Find only soft-deleted recordsconst deletedPosts = await postRepo.findDeleted().execute(orm.client);console.log("Deleted posts:", deletedPosts.length);
// Count deleted recordsconst deletedCount = await postRepo.countDeleted();console.log("Deleted count:", deletedCount);Recover Deleted Records
examples/recover.ts
// Recover a single recordconst recovered = await postRepo.recover(post.id);console.log("Recovered:", recovered.title);
// Recover all soft-deleted recordsconst recoveredCount = await postRepo.recoverAll();console.log("Recovered", recoveredCount, "records");Bulk Soft Delete
examples/bulk-delete.ts
// Bulk soft delete multiple recordsawait 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 recordScopes with Soft Delete
examples/scopes-soft-delete.ts
// The find() method automatically excludes soft-deleted recordsconst active = await postRepo.find().execute(orm.client);// WHERE deletedAt IS NULL is automatically added
// Use findDeleted() to get only deleted recordsconst deleted = await postRepo.findDeleted().execute(orm.client);
// Other query methods also respect soft deleteconst count = await postRepo.count(); // excludes deletedconst exists = await postRepo.exists({ title: "My Post" }); // excludes deletedconst page = await postRepo.paginate(1, 10); // excludes deleted