An AI assistant wrote the label route, the text for the shop's shelf: GET /products/1/label answers {"label": "Mug (kitchen)"}. It put its code in a try, which is a good idea: when something fails, the route still answers. But look at what its catch sends back:
app.get("/products/:productId/label", (req: Request, res: Response) => {
const productId = Number(req.params.productId);
const query = "SELECT name, category FROM products WHERE id = ?";
try {
const row = db.prepare(query).get(productId) as ProductRow | undefined;
const label = row.name + " (" + row.category.toLowerCase() + ")";
res.json({ label: label });
} catch (error) {
res.status(500).json({ error: error.message, sql: query, trace: error.stack });
}
});
That answer goes to whoever made the request. It holds the error's text, the SQL query and the stack trace: the files, line numbers and functions that led to the crash. A customer can't do anything with it. An attacker can: file paths, table and column names and the names of your functions are a free map for finding the next hole.
The rule: the details go to the log, which only you read, and the visitor gets a short safe message. Here it is on another route:
} catch (error) {
console.error("order " + orderId + " failed: " + error.message);
res.status(500).json({ error: "Please try again later" });
}
A missing product is not a server error, so it answers 404, not 500. Your rewrite must give these answers (product 8 is a Gift card with no category, a bug nobody had found yet: its type says category: string | null, but the course compiles without null checks, so TypeScript doesn't stop it):
GET /products/1/label → 200 {"label": "Mug (kitchen)"}
GET /products/6/label → 200 {"label": "Teapot (kitchen)"}
GET /products/8/label → 500 {"error": "Something went wrong on our side"}
GET /products/99/label → 404 {"error": "Product not found"}
The check then reads your log through /_test/log, a route it adds only for the test, and expects one line: ERROR GET /products/8/label failed: Cannot read properties of null (reading 'toLowerCase').
Your task: rewrite the label route so any error answers only {"error": "Something went wrong on our side"} with 500 and logs the method, the path and the error's message, while a missing product answers 404.
Assignment
Follow the Coding Tutorial to rewrite the label route with a try that builds the label and a catch that logs the details and answers a safe message.
Hint
A try can't stand alone: close it with } catch (error) { and a }. End the row line with as ProductRow | undefined. The 404 check sits inside the try, right below the row line, and starts its answer with return. Pass console.error one text: the method, a space, the path, " failed: " and error.message, joined with +. In the catch, error has the type any, so TypeScript lets you read .message.
Free plan: 30 tutor messages. PRO is unlimited.
