CodingNic

Categories and Summary

Add a Category Safely

Categories and Summary 18 min read

Add a Category Safely

Open:

text
server/src/routes/categories.js
client/src/lib/api.js
client/src/context/BudgetContext.jsx
client/src/pages/CategoriesPage.jsx

Adding a category is a small create workflow: read a name, send it to the API, receive the canonical category object, then update shared state.

Trim the submitted name before doing any checks:

javascript
const name = typeof req.body?.name === "string"
  ? req.body.name.trim()
  : "";

if (!name) {
  return res.status(400).json({ error: "name is required" });
}

This prevents a category made only of spaces.

Now treat capitalization as presentation rather than identity:

javascript
const existing = db.categories.find(
  (category) => category.name.toLowerCase() === name.toLowerCase()
);

if (existing) {
  return res.status(200).json(existing);
}

If Travel already exists, submitting travel returns the same category instead of creating a second one.

When the name is new, create and persist the category:

javascript
const category = { name, color };
db.categories.push(category);
await saveDb();
res.status(201).json(category);

The API helper sends the request:

javascript
export const addCategory = (name) =>
  request("/categories", {
    method: "POST",
    body: JSON.stringify({ name }),
  });

The context updates its shared collection using the returned category:

javascript
async function addCategory(name) {
  const category = await api.addCategory(name);

  setCategories((previous) =>
    previous.some(
      (item) => item.name.toLowerCase() === category.name.toLowerCase()
    )
      ? previous
      : [...previous, category]
  );

  return category.name;
}

The page should clear the input only after the request succeeds. That way a failed request does not erase what the user typed.

Test it

Add Travel.

Add travel again. The second action should not create a duplicate category.

Refresh the page and confirm the category remains.

Checkpoint

A category name is trimmed, duplicate names are handled consistently, the server owns the canonical category object, and successful creation updates shared client state.