Start learning free

LESSON 163 OF 173 · BACKEND: BACKEND WITH AI

The AI Shows Too Much in TypeScript

Practice

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.


STEP 1 OF 9
    Ctrl + Enter to check

    Ai Leaky Errors in TypeScript

    Keep going with TypeScript1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free