In a full-stack shop, "not found" can mean three different things, and each one has its own right answer. An unknown page, like /nope, belongs to the storefront: React Router's * route shows "Page not found", and the API never hears about it. An unknown product, like /products/99, is a real page asking the API for something that isn't there: the API must answer 404 with a JSON message, and the page shows it. An unknown API path, like /api/nope, must still get JSON, because the page reads every /api answer as JSON. Express's own 404 is an HTML page.
Today the product route doesn't check. For id 99, .get() gives undefined, and res.json(undefined) answers 200 with an empty body. The page then shows "Unexpected end of JSON input". A route that can miss checks first, like this one for customers:
app.get("/api/customers/:id", (req, res) => {
const customer = db.prepare("SELECT * FROM customers WHERE id = ?").get(req.params.id);
if (customer === undefined) {
return res.status(404).json({ error: "No such customer" });
}
res.json(customer);
});
For unknown API paths, remember that Express tries handlers in the order of the file and stops at the first one that answers. A handler given to app.use with a path like "/api" runs for every request under that path that gets that far. Below the last route, it only gets the /api requests no route answered.
Your app must answer:
open /nope → the storefront's "Page not found"
open /products/1 → the page shows Mug, $12
open /products/99 → the page shows "No such product"
GET /api/products/99 → 404 {"error": "No such product"}
GET /api/nope → 404 {"error": "Not found"}
Your task: in server/app.js, make GET /api/products/:id answer 404 {"error": "No such product"} when there is no such product. Then write a function notFound that answers 404 {"error": "Not found"}, and give it to app.use for "/api", below the last route.
Assignment
Follow the Coding Tutorial to add the missing-product check to the product route, then write notFound and put it after every /api route.
Hint
Without return, the route would go on to res.json(product) and answer twice. notFound goes below the last route, but above app.use(handleError).
Free plan: 30 tutor messages. PRO is unlimited.
