The cache saves the server's work, but the shop still sends the whole product list every time, even to a browser that got the very same list a minute ago. An ETag fixes that: a short fingerprint of an answer, sent in a header next to it. The same content always gives the same ETag; change one price and it is completely different.
A browser that has a copy sends its ETag back in the If-None-Match header, meaning "send it only if it isn't this one". If the fingerprint still matches, the server answers 304 Not Modified with an empty body, and the browser shows its own copy. A few bytes travel instead of the whole list.
One place for every route: Express calls a middleware, a function given to app.use, for every request, before its route; next() lets the request go on to its route. For example, this one adds a header to every answer:
app.use((req, res, next) => {
res.set("X-Shop", "AlgoShop");
next();
});
The fingerprint needs the answer the route makes, and every answer leaves through one function: res.json turns the object into text and hands that text to res.end(body), which sends it. So your middleware keeps the real res.end and puts its own function in its place. (Express can add ETags by itself; the starting code switches that off with app.set("etag", false), so that you build one and see how it works.)
Your code must give these answers, in this order. The 304s save the trip, not the query: the cache still does that job, so the count stays at 4.
GET /products → 200 ETag: "d62f8998b9c6175bf98ed96c8fa6de98" [all 8 products, Mug at 12]
GET /products If-None-Match: "d62f8998b9c6175bf98ed96c8fa6de98" → 304 ETag: "d62f8998b9c6175bf98ed96c8fa6de98" (empty body)
PATCH /products/1 {"price": 13} → 200 {"id": 1, "price": 13} (no ETag)
GET /products If-None-Match: "d62f8998b9c6175bf98ed96c8fa6de98" → 200 ETag: "426305b11dfff0b0d6b5151683164898" [all 8 products, Mug at 13]
GET /products If-None-Match: "426305b11dfff0b0d6b5151683164898" → 304 ETag: "426305b11dfff0b0d6b5151683164898" (empty body)
GET /products/99 → 404 {"error": "Product not found"} (no ETag)
GET /debug/queries → 200 {"queries": 4}
Your task: below runQuery, add a middleware with app.use that puts its own function in place of res.end. For a GET that answered 200, hash the answer's text with crypto.createHash("md5") and send the hash, between double quotes, as the ETag header. If the request's If-None-Match header is that same ETag, answer 304 with an empty body.
Assignment
Follow the Coding Tutorial to add ETags and 304 answers to the shop's successful GET requests.
Hint
Keep the real function first with res.end.bind(res), and call next() so the request goes on to its route. .update(body).digest("hex") turns the answer's text into a hash, res.set("ETag", etag) writes the header, and req.get("If-None-Match") reads the browser's header.
Free plan: 30 tutor messages. PRO is unlimited.
