Single Record Deletion
Imagine you're managing an inventory system and need to remove discontinued products:
// Basic single record deletion
const deleteProduct = async (productId) => {
// Find the product
const product = await Product.findByPk(productId);
if (!product) {
throw new Error('Product not found');
}
// Delete the product
await product.destroy();
return { message: 'Product successfully deleted' };
};
// With soft delete (paranoid)
const softDeleteProduct = async (productId) => {
const product = await Product.findByPk(productId);
if (!product) {
throw new Error('Product not found');
}
await product.destroy({ force: false }); // Soft delete
return { message: 'Product archived' };
};
Multiple Record Deletion
Like clearing out all expired items from inventory:
// Delete multiple records meeting certain criteria
const removeExpiredProducts = async () => {
const result = await Product.destroy({
where: {
expiryDate: {
[Op.lt]: new Date()
}
}
});
return { deletedCount: result };
};
// Delete with transaction
const bulkDeleteProducts = async (categoryId) => {
const t = await sequelize.transaction();
try {
const result = await Product.destroy({
where: { categoryId },
transaction: t
});
await t.commit();
return { deletedCount: result };
} catch (error) {
await t.rollback();
throw error;
}
};
Real-World Examples
User Account Deletion System
const deleteUserAccount = async (userId) => {
const t = await sequelize.transaction();
try {
// Find user with associated data
const user = await User.findByPk(userId, {
include: [
{ model: Post },
{ model: Comment },
{ model: Profile }
],
transaction: t
});
if (!user) {
throw new Error('User not found');
}
// Archive important data
await UserArchive.create({
userId: user.id,
userData: JSON.stringify(user.toJSON()),
archivedAt: new Date()
}, { transaction: t });
// Delete associated data
await Promise.all([
Post.destroy({ where: { userId }, transaction: t }),
Comment.destroy({ where: { userId }, transaction: t }),
Profile.destroy({ where: { userId }, transaction: t })
]);
// Finally, delete user
await user.destroy({ transaction: t });
await t.commit();
return { message: 'Account successfully deleted' };
} catch (error) {
await t.rollback();
throw error;
}
};
E-commerce Order Cleanup System
const cleanupAbandonedOrders = async () => {
const TWO_HOURS = 2 * 60 * 60 * 1000;
const result = await Order.destroy({
where: {
status: 'pending',
createdAt: {
[Op.lt]: new Date(Date.now() - TWO_HOURS)
}
},
include: [{
model: OrderItem,
required: true
}]
});
return {
message: `Cleaned up ${result} abandoned orders`
};
};
Paranoid Delete (Soft Delete)
// Model definition with paranoid option
const Product = sequelize.define('Product', {
name: DataTypes.STRING,
price: DataTypes.DECIMAL
}, {
paranoid: true // Enables soft delete
});
// Implementing soft delete functionality
const softDeleteManager = {
delete: async (model, id) => {
const record = await model.findByPk(id);
if (!record) throw new Error('Record not found');
await record.destroy(); // Performs soft delete
return { message: 'Record archived' };
},
restore: async (model, id) => {
const record = await model.findByPk(id, {
paranoid: false // Include soft-deleted records
});
if (!record) throw new Error('Record not found');
await record.restore();
return { message: 'Record restored' };
},
forceDelete: async (model, id) => {
const record = await model.findByPk(id, {
paranoid: false
});
if (!record) throw new Error('Record not found');
await record.destroy({ force: true });
return { message: 'Record permanently deleted' };
}
};
Cascade Deletion
// Setting up cascade delete in model associations
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
content: DataTypes.TEXT
});
Post.associate = (models) => {
Post.hasMany(models.Comment, {
foreignKey: 'postId',
onDelete: 'CASCADE'
});
};
return Post;
};
// Using cascade delete
const deletePostWithComments = async (postId) => {
const post = await Post.findByPk(postId);
if (!post) throw new Error('Post not found');
// This will automatically delete all associated comments
await post.destroy();
return { message: 'Post and comments deleted' };
};
Best Practices
- Use Transactions: Wrap related deletions in transactions
- Validate Before Delete: Always check if records exist
- Consider Soft Delete: Use paranoid option for sensitive data
- Handle Dependencies: Consider cascade effects
- Archive Important Data: Keep records of deleted data when necessary
Common Pitfalls to Avoid
- Not handling cascading deletes properly
- Forgetting to use transactions
- Missing WHERE clauses in bulk deletes
- Not considering data relationships
- Inadequate error handling
Advanced Topics to Explore
- Custom deletion hooks
- Batch deletion strategies
- Recovery mechanisms
- Audit trails
- Performance optimization