Imagine you're managing a library catalog. Sometimes books get too old or damaged and need to be removed from the system. In our case, we need to create a route that can remove a tree from our database. This involves:
When we delete a tree, one of two things can happen:
1. The tree exists and is successfully deleted - we should return a success message
2. The tree doesn't exist - we should return a friendly "not found" message
Expected Input:
DELETE request to /trees/5 // where 5 is the tree's ID
Expected Success Output:
{
"status": "success",
"message": "Successfully removed tree"
}
Expected Error Output (Tree Not Found):
{
"status": "not-found",
"message": "Could not remove tree 5",
"details": "Tree not found"
}
// DELETE route to remove a tree by ID
router.delete('/:id', async (req, res, next) => {
try {
// First, try to delete the tree
const deletedTree = await Tree.destroy({
where: {
id: req.params.id
}
});
// destroy() returns the number of rows deleted
if (deletedTree === 0) {
// If no trees were deleted, the tree wasn't found
const error = new Error(`Could not remove tree ${req.params.id}`);
error.status = "not-found";
error.details = "Tree not found";
throw error;
}
// If we get here, the tree was successfully deleted
res.json({
status: "success",
message: "Successfully removed tree"
});
} catch (err) {
// Pass any errors to the error handler
next(err);
}
});
Let's break down the key concepts in our delete operation:
Sequelize's destroy() method is like a vacuum cleaner - it removes records from our database. It returns a number telling us how many records it "cleaned up":
We're using a "throw and catch" pattern for error handling. Think of it like a game of hot potato:
Delete operations are common in many applications:
You should test your delete route in several ways:
npm test test/phase-03-spec.jsWatch out for these common issues when implementing delete operations:
Here's a more sophisticated version that includes additional safety checks and features:
router.delete('/:id', async (req, res, next) => {
try {
// Validate ID format
const id = parseInt(req.params.id);
if (isNaN(id)) {
throw new Error('Invalid ID format');
}
// Use a transaction for safety
const result = await sequelize.transaction(async (t) => {
// First check if the tree exists
const tree = await Tree.findByPk(id, { transaction: t });
if (!tree) {
throw new Error(`Tree ${id} not found`);
}
// Optional: Add soft delete flag
// await tree.update({ deleted: true }, { transaction: t });
// Perform the actual delete
await tree.destroy({ transaction: t });
return tree; // Return the deleted tree info if needed
});
res.json({
status: "success",
message: "Successfully removed tree",
data: {
id: result.id,
name: result.tree
}
});
} catch (err) {
next({
status: err.message.includes('not found') ? 'not-found' : 'error',
message: `Could not remove tree ${req.params.id}`,
details: err.message
});
}
});
To better understand DELETE operations, try implementing these features:
Consider these questions to deepen your understanding: