An AI assistant wrote the product page for this shop. Its handler for GET /products/{id} asks the database for one product and answers its name, its price and whether it is in stock. Here is the AI's version:
app.HandleFunc("GET /products/{id}", func(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(r.PathValue("id"))
var p Product
db.QueryRow("SELECT * FROM products WHERE id = ?", id).Scan(&p.ID, &p.Name, &p.Price, &p.Category, &p.Stock)
writeJSON(w, 200, map[string]any{"name": p.Name, "price": p.Price, "in_stock": p.Stock > 0})
})
It works for every product that exists. But when no product has the id, Scan returns the error sql.ErrNoRows and copies nothing into p, so p stays empty: "" for the name, 0 for the price and the stock. The AI never looks at that error. Nothing crashes: the handler answers 200 with {"in_stock": false, "name": "", "price": 0}, a product that doesn't exist. The AI wrote the happy path and forgot the missing case.
The fix is a check that runs before the code uses the value, with a return after the error answer: writing an answer doesn't end a Go handler. Here it is on another route, for coupons kept in a map:
app.HandleFunc("GET /coupons/{code}", func(w http.ResponseWriter, r *http.Request) {
coupon, ok := coupons[r.PathValue("code")]
if !ok {
writeJSON(w, 404, map[string]string{"error": "Coupon not found"})
return
}
writeJSON(w, 200, coupon)
})
A 404 says "there is no such thing", in JSON the client can read. Your code must give these answers:
GET /products/1 → 200 {"in_stock": true, "name": "Mug", "price": 12}
GET /products/2 → 200 {"in_stock": false, "name": "Notebook", "price": 7}
GET /products/8 → 200 {"in_stock": true, "name": "Gift card", "price": 50}
GET /products/99 → 404 {"error": "Product not found"}
GET /products/0 → 404 {"error": "Product not found"}
Your task: rewrite the GET /products/{id} handler. Scan the product with this id into p and keep the error Scan returns in err. If err is sql.ErrNoRows, answer {"error": "Product not found"} with 404 and return; otherwise answer its name, its price and in_stock (true when the stock is above 0).
Assignment
Follow the Coding Tutorial to rewrite the GET /products/{id} handler with the missing case handled.
HintScan returns sql.ErrNoRows when no row matches. Check err == sql.ErrNoRows before the 200 answer, and end the if with return so the 200 line doesn't run too.
Free plan: 30 tutor messages. PRO is unlimited.
