Imagine you're maintaining a library catalog where book details occasionally need to be corrected or updated. Similarly, in our tree database, we sometimes need to update information about existing trees. This is like editing a document rather than creating a new one or throwing it away.
When updating a tree, we need to handle several scenarios:
Expected Input Example:
PUT /trees/3
{
"id": 3,
"name": "President-edit",
"location": "Sequoia National Park-edit",
"height": 240.91,
"size": 93.1
}
Expected Success Output:
{
"status": "success",
"message": "Successfully updated tree",
"data": {
"id": 3,
"tree": "President-edit",
"location": "Sequoia National Park-edit",
"heightFt": 240.91,
"groundCircumferenceFt": 93.1,
"updatedAt": "2025-02-17T..."
}
}
Let's break this down into manageable steps:
router.put('/:id', async (req, res, next) => {
try {
// Extract data from request
const treeId = parseInt(req.params.id);
const { id, name, location, height, size } = req.body;
// Verify IDs match
if (id !== treeId) {
throw new Error(`${treeId} does not match ${id}`);
}
// Find the tree
const tree = await Tree.findByPk(treeId);
// Handle tree not found
if (!tree) {
const err = new Error(`Could not update tree ${treeId}`);
err.status = "not-found";
err.details = "Tree not found";
throw err;
}
// Update tree properties
tree.tree = name; // Map 'name' to 'tree'
tree.location = location;
tree.heightFt = height; // Map 'height' to 'heightFt'
tree.groundCircumferenceFt = size; // Map 'size' to 'groundCircumferenceFt'
// Save changes
await tree.save();
// Return success response
res.json({
status: "success",
message: "Successfully updated tree",
data: tree
});
} catch (err) {
next(err);
}
});
Updating a record in Sequelize typically follows a pattern we can think of as "Find, Modify, Save":
Just like in our create operation, we need to map between the API fields and database fields:
| API Field | Database Field |
|---|---|
| name | tree |
| height | heightFt |
| size | groundCircumferenceFt |
Here's a more robust version that includes additional error checking and partial updates:
router.put('/:id', async (req, res, next) => {
try {
const treeId = parseInt(req.params.id);
const { id, name, location, height, size } = req.body;
// Input validation
if (isNaN(treeId)) {
throw new Error('Invalid ID format');
}
// ID matching check
if (id && id !== treeId) {
throw new Error(`${treeId} does not match ${id}`);
}
// Use a transaction for safety
const result = await sequelize.transaction(async (t) => {
const tree = await Tree.findByPk(treeId, { transaction: t });
if (!tree) {
const err = new Error(`Could not update tree ${treeId}`);
err.status = "not-found";
err.details = "Tree not found";
throw err;
}
// Only update provided fields (partial update)
if (name !== undefined) tree.tree = name;
if (location !== undefined) tree.location = location;
if (height !== undefined) tree.heightFt = height;
if (size !== undefined) tree.groundCircumferenceFt = size;
// Validate before saving
await tree.validate();
// Save changes
await tree.save({ transaction: t });
return tree;
});
res.json({
status: "success",
message: "Successfully updated tree",
data: result
});
} catch (err) {
next({
status: err.status || "error",
message: err.message || "Could not update tree",
details: err.details || err.errors?.map(e => e.message).join(', ')
});
}
});
Update operations are essential in many real-world scenarios:
To thoroughly test your update route, try these scenarios:
Be careful to avoid these common issues:
Try implementing these enhancements to deepen your understanding:
Consider these questions to deepen your understanding: