Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Notes: PUT /users/:id

## Plan

The task was to add an "update a user" endpoint: `PUT /users/:id`, validating
input, returning 404 for an unknown user, and going through `db/store.js`
like the existing routes. The plan was to add a small `updateUser(id, {name,
email})` helper to the store (mirroring the existing `getUserById` /
`createUser` helpers), then add the `PUT /:id` route in `routes/users.js`,
validating `name`/`email` the same way `POST /users` already does before
checking whether the user exists. I didn't need to change the plan — the
existing GET/POST routes already established a clear pattern to follow, so
the shape of the change was obvious from reading the codebase first.

## Model

Used the session's default model (Claude Opus 5). This is a small,
well-specified change against an existing, very consistent pattern in a
tiny codebase, so a lighter/faster model would likely have worked just as
well — I didn't need extra reasoning depth here, just careful pattern
matching against the existing GET/POST routes and the provided tests.

## Commit split

Two commits, one per layer, matching how the codebase already separates
data access from routing:

1. `db/store.js` — add the `updateUser` helper (data layer).
2. `routes/users.js` — add the `PUT /:id` route that uses it (HTTP layer).

Keeping them separate makes each commit reviewable on its own: the first
is pure data-layer logic ("how do we update a user"), the second is pure
request handling ("how do we expose that over HTTP").

## Review

Self-reviewed by running `npm test` and `npm run lint` after implementing:
- All three `update-user.test.js` cases pass (200 update, 404 for unknown
id, 400 for a missing field), and the pre-existing tests (health, users
list/404) still pass — no regressions.
- Checked the validation-vs-not-found ordering: the endpoint validates
input first (matching `POST /users`), then looks the user up. The given
tests don't exercise "invalid body + unknown id" together, but validating
first keeps the behavior consistent with the existing POST route rather
than introducing a different rule just for this endpoint.
- Confirmed `updateUser` mutates the existing user object in place (via
`getUserById`) rather than replacing it in the array, consistent with how
the in-memory store already works elsewhere in `db/store.js`.
- Lint is clean (`npm run lint`), so no style issues were introduced.
14 changes: 13 additions & 1 deletion db/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,16 @@ function createUser({ name, email }) {
return user;
}

module.exports = { getAllUsers, getUserById, createUser };
function updateUser(id, { name, email }) {
const user = getUserById(id);

if (!user) {
return undefined;
}

user.name = name;
user.email = email;
return user;
}

module.exports = { getAllUsers, getUserById, createUser, updateUser };
18 changes: 18 additions & 0 deletions routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,22 @@ router.post("/", (req, res) => {
res.status(201).json(user);
});

// PUT /users/:id — update an existing user; name and email are required
router.put("/:id", (req, res) => {
const { name, email } = req.body;

if (!name || !email) {
return res.status(400).json({ error: "name and email are required" });
}

const id = Number(req.params.id);
const user = store.updateUser(id, { name, email });

if (!user) {
return res.status(404).json({ error: "User not found" });
}

res.json(user);
});

module.exports = router;