WDX-180
Web Development X
Updating Products (UPDATE)
Completing the Second Quarter of CRUD
Creating data is exciting.
Updating data is where reality begins.
In the real world:
- Product prices change
- Descriptions improve
- Inventory evolves
- Mistakes happen
Lots of mistakes.
If your application can only create products, eventually users start creating:
Keyboard
Keyboard v2
Keyboard v3
Keyboard FINAL
Keyboard FINAL FINAL
Keyboard FINAL USE THIS ONE
Congratulations.
You’ve accidentally recreated Microsoft Word document versioning.
Today we learn how to update existing records properly.
Learning Objectives
By the end of this lesson, students will be able to:
- Understand the Update portion of CRUD
- Build edit forms
- Pre-populate forms with existing data
- Process update requests
- Execute SQL UPDATE statements
- Validate incoming data
- Handle missing records
- Reuse form templates
- Understand optimistic thinking versus defensive programming
What We Are Building
Current:
flowchart TD
A[Product Page]
A --> B[View Product]
After today:
flowchart TD
A[Product Page]
A --> B[Edit Product]
B --> C[Update Database]
C --> D[View Updated Product]
Part 1 — Understanding UPDATE
Creating records:
INSERT INTO products (...)
adds new data.
Updating records:
UPDATE products
SET ...
WHERE ...
modifies existing data.
Example:
Before:
Mechanical Keyboard
$89.99
After:
Mechanical Keyboard Pro
$99.99
Same record.
New values.
Part 2 — SQL UPDATE
Basic syntax:
UPDATE products
SET
name = ?,
description = ?,
price = ?
WHERE id = ?
Very important:
WHERE id = ?
Without it:
UPDATE products
SET price = 0
becomes:
Every product now costs $0
Customers love this.
Management usually does not.
Part 3 — The Edit Route
URL:
/products/edit/5
Route:
router.get('/edit/:id', (req, res) => {
const product = productRepository.findById(req.params.id);
if (!product) {
return res
.status(404)
.render('404');
}
res.render('products/edit',
{
title: "Edit Product",
product
}
);
});
Why Load the Product First?
Users need to see:
Current Values
before editing.
Without loading:
Name:
[____________]
With loading:
Name:
[Mechanical Keyboard]
Much better.
Part 4 — Building the Edit Form
views/products/edit.ejs
<h2>Edit Product</h2>
<form
action="/products/edit/<%= product.id %>"
method="post"
>
<div>
<label>Name</label>
<input
type="text"
name="name"
value="<%= product.name %>"
>
</div>
<div>
<label>Description</label>
<textarea
name="description"
><%= product.description %></textarea>
</div>
<div>
<label>Price</label>
<input
type="number"
step="0.01"
name="price"
value="<%= product.price %>"
>
</div>
<button type="submit">
Save Changes
</button>
</form>
Understanding Pre-Populated Forms
The form displays:
value="<%= product.name %>"
instead of:
value=""
This is how nearly every edit form on the internet works.
Part 5 — Processing Updates
Route:
router.post('/edit/:id', (req, res) => {
const {
name,
description,
price
} = req.body;
const id =
req.params.id;
productRepository.update(
id,
name,
description,
price
);
res.redirect(
`/products/${id}`
);
});
Repository Function
function update(
id,
name,
description,
price
) {
const stmt = db.prepare(`
UPDATE products
SET
name = ?,
description = ?,
price = ?
WHERE id = ?
`);
return stmt.run(
name,
description,
price,
id
);
}
What Does run() Return?
Example:
{
changes: 1
}
Meaning:
1 row updated
If:
{
changes: 0
}
then nothing changed.
Possibly because the product doesn’t exist.
Part 6 — Validation
Updating requires validation just like creating.
❌ Bad:
{
name: '',
price: ''
}
❌ Bad:
{
name: 'Keyboard',
price: -100
}
Validation:
if (!name) {
return res.render(
'products/edit',
{
title: "Edit Product",
error: 'Name is required.',
product: req.body
}
);
}
Price Validation:
const numericPrice = Number(price);
if (Number.isNaN(numericPrice)) {
return res.render(...);
}
Part 7 — Preserving User Input
Suppose:
Name OK
Price Invalid
Validation fails.
Bad UX:
All form data disappears
Good UX:
res.render(
'products/edit',
{
error: 'Invalid price',
product: {
id,
name,
description,
price
}
}
);
The form remains populated.
Users remain calm.
Part 8 — Adding Edit Links
Product page:
<a href="/products/edit/<%= product.id %>">Edit Product</a>
List page:
<a href="/products/edit/<%= product.id %>">Edit</a>
Now editing is accessible everywhere.
Part 9 — Reusing Forms
Notice:
create.ejs
and
edit.ejs
look almost identical.
Professional developers avoid duplication.
Example Partial:
views/products/_form.ejs
<input name="name" value="<%= product?.name || '' %>">
Create:
<%- include('_form',{ product: {} }) %>
Edit:
<%- include('_form',{ product }) %>
One form.
Two use cases.
Less maintenance.
Part 10 — Understanding Idempotency
Interesting concept:
Create:
POST /products/create
twice
creates:
Two Products
Update:
POST /products/edit/5
with same values twice
results in:
Same Product State
This property is called:
Idempotency
A useful concept for APIs.
Part 11 — Defensive Programming
Imagine:
/products/edit/999999
Repository:
const result = productRepository.update(...);
Check:
if ( result.changes === 0) {
return res
.status(404)
.render('404');
}
Never assume records exist.
Verify.
Part 12 — Audit Thinking
Imagine:
Original Price
$100
User changes:
$10
Question:
Who changed it?
When?
Why?
Most CRUD tutorials ignore this.
Real businesses care deeply.
Future systems often add:
updated_at
updated_by
columns.
We’ll keep things simple for now.
Common Beginner Mistakes
Forgetting WHERE
Bad:
UPDATE products
SET price = 0
Dangerous.
Not Checking Existence
Bad:
update(id)
without verifying product exists.
Repeating Form Code
Create and Edit forms often become duplicates.
Extract shared partials.
Skipping Validation
Updates require validation just as much as creation.
Trusting Route Parameters
Validate:
req.params.id
before using them.
Bonus Challenge
Add:
updated_at
to the table.
Schema:
ALTER TABLE products
ADD COLUMN updated_at DATETIME;
Update query:
UPDATE products
SET
name = ?,
description = ?,
price = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?
Display:
Last Updated:
on the product page.
This is the first step toward real audit tracking.
Key Takeaways
Today you learned:
- SQL UPDATE
- Edit forms
- Pre-populated forms
- Validation during updates
- Redirects
- Shared form partials
- Idempotency
- Defensive programming
- Audit thinking
At this point, your CMS can:
Create Products
Read Products
Update Products
Three quarters of CRUD are complete.
The application is beginning to resemble the core of a real content management system rather than a collection of isolated pages.
⚠️ A large part of the content of this module was created using Generative AI (ChatGPT). The synthetic (AI-generated) content was reviewed and curated by Kostas Minaidis.