Look at your handlers. Almost every one runs db.Query(...), closes the rows with defer rows.Close(), loops with rows.Next() and scans each row. Every handler types the same moves again. A helper is a small function that makes those moves once, so each handler gets its rows in one line.
Any number of values: a last parameter written name ...type takes any number of values, zero included, and holds them as a slice. A call can pass none, one or many. For example:
func total(prices ...int) int {
sum := 0
for _, price := range prices {
sum = sum + price
}
return sum
}
total(12, 3) // prices holds 12 and 3: 15
total() // prices is empty: 0
To pass on a slice you already have, write ... after it: db.Query(text, params...). Your helpers are queryAll(text string, params ...any) []map[string]any, which answers one map per row (the given rowMap turns a row into a map), and queryOne, which answers only the first map, or nil when no row matched. In this lesson the check also calls your helpers itself (/check/helper):
GET /products?category=kitchen → 200 {"products": [{"id": 1, "name": "Mug", …}, {"id": 6, "name": "Teapot", …}], "total": 2, "next_page": null}
GET /products?name=Sofa → 200 {"products": [], "total": 0, "next_page": null}
GET /products/3 → 200 {"id": 3, "name": "Lamp", "price": 29, "category": "home", "stock": 1, "discount": 0}
GET /products/99 → 404 {"error": "Product not found"}
GET /check/helper → 200 {"many": [{"id": 2, "name": "Notebook"}, {"id": 4, "name": "Pen"}, {"id": 7, "name": "Stapler"}], "empty": [], "one": {"name": "Eva Rossi", "city": "Rome"}, "missing": null, "no_params": {"customers": 5}}Your task: below rowMap, write queryAll: it runs the SQL with its params and answers a slice of maps, one per row. Then write queryOne, with the same parameters: it answers the first map, or nil. Make the GET /products handler get its page from queryAll, and the GET /products/{id} handler get its row from queryOne, in one line each.
Assignment
Follow the Coding Tutorial to write queryAll and queryOne, and use them in the two product handlers.
Hint
Build the whole slice of maps in queryAll first; queryOne only takes its first item. Remember that result[0] on an empty slice panics, so return nil when len(result) is 0.
Free plan: 30 tutor messages. PRO is unlimited.
