CodingNic

API, Persistence, and Hardening

Enforce Category Existence on the Server

API, Persistence, and Hardening 16 min read

Enforce Category Existence on the Server

The category <select> in React shows only known categories, but that is not enough. A client can send any JSON it wants. The server therefore needs to check the relationship between an expense and the category list before saving the transaction.

Open server/src/domain/transactions.js. The validator already receives both the request body and the current categories:

javascript
export function validateTransactionInput(body, categories) {
  // validate the common transaction fields first

For income, the category must be empty because income does not belong to an expense category:

javascript
if (type === "income") {
  if (category !== null && category !== undefined && category !== "") {
    return "income transactions must not have a category";
  }
  return null;
}

For expenses, require a category name and then search the current category list:

javascript
if (typeof category !== "string" || !category.trim()) {
  return "category is required for expenses";
}

const categoryExists = categories.some(
  (item) => item.name === category.trim()
);

if (!categoryExists) return "category does not exist";

some() stops as soon as it finds a matching category. The important rule is not the method itself; it is that the server checks the relationship against the data it is about to persist.

The route calls the validator inside updateDb(), so a failed validation throws before the transaction is pushed into the array or written to disk.

Test it

Try to create an expense using a category name that does not exist, such as Travel. The server should respond with an error instead of creating the transaction.

Then add Travel through the Categories page and create the expense again. The same request should now succeed.

Checkpoint

You now have a server-side integrity rule: expenses can reference only existing categories, while income deliberately has no category.