How to Implement Soft Delete in Express: Simple Guide
To implement
soft delete in Express, add a flag like isDeleted to your data model and update it instead of removing the record. Then, filter queries to exclude records where isDeleted is true, keeping data safe but hidden.Syntax
Soft delete involves adding a boolean field like isDeleted to your data model. Instead of deleting a record, you update this field to true. When fetching data, you filter out records where isDeleted is true.
This pattern keeps data in the database but hides it from normal views.
javascript
app.delete('/items/:id', async (req, res) => { const id = req.params.id; await Item.updateOne({ _id: id }, { isDeleted: true }); res.send({ message: 'Item soft deleted' }); }); app.get('/items', async (req, res) => { const items = await Item.find({ isDeleted: false }); res.send(items); });
Example
This example shows a simple Express app with a MongoDB model using Mongoose. It implements soft delete by setting isDeleted to true instead of removing the item. The GET route only returns items not marked as deleted.
javascript
import express from 'express'; import mongoose from 'mongoose'; const app = express(); app.use(express.json()); // Connect to MongoDB mongoose.connect('mongodb://localhost:27017/softdelete').then(() => { console.log('Connected to MongoDB'); }).catch(err => { console.error('MongoDB connection error:', err); }); // Define schema with isDeleted flag const itemSchema = new mongoose.Schema({ name: String, isDeleted: { type: Boolean, default: false } }); const Item = mongoose.model('Item', itemSchema); // Soft delete route app.delete('/items/:id', async (req, res) => { const id = req.params.id; const result = await Item.updateOne({ _id: id }, { isDeleted: true }); if (result.modifiedCount === 0) { return res.status(404).send({ message: 'Item not found' }); } res.send({ message: 'Item soft deleted' }); }); // Get all non-deleted items app.get('/items', async (req, res) => { const items = await Item.find({ isDeleted: false }); res.send(items); }); // Start server app.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
Output
Server running on http://localhost:3000
// After adding items and deleting one:
// GET /items returns only items with isDeleted: false
Common Pitfalls
- Forgetting to filter out soft deleted records in queries causes deleted data to show.
- Not adding the
isDeletedfield to the schema or model leads to errors. - Using
deleteOneorremoveinstead of updating the flag deletes data permanently. - Not handling errors when updating the flag can cause silent failures.
javascript
/* Wrong: Permanently deleting item */ app.delete('/items/:id', async (req, res) => { await Item.deleteOne({ _id: req.params.id }); res.send({ message: 'Item deleted permanently' }); }); /* Right: Soft delete by updating flag */ app.delete('/items/:id', async (req, res) => { const result = await Item.updateOne({ _id: req.params.id }, { isDeleted: true }); if (result.modifiedCount === 0) { return res.status(404).send({ message: 'Item not found' }); } res.send({ message: 'Item soft deleted' }); });
Quick Reference
- Add a boolean
isDeletedfield to your data model. - Update
isDeletedtotrueinstead of deleting. - Filter queries to exclude records where
isDeletedistrue. - Handle errors when updating the flag.
Key Takeaways
Use a boolean flag like isDeleted to mark records as deleted instead of removing them.
Always filter your queries to exclude soft deleted records to hide them from users.
Never use deleteOne or remove if you want to keep data for recovery or audit.
Handle update errors to avoid silent failures during soft delete.
Soft delete helps keep data safe while allowing it to be restored or audited later.