This project puts the module together in the shop's busiest route, GET /products. Earlier lessons made the list ask only for id, name, price and read one keyset page at a time with ?after=. Two things are still missing: clients can't choose the page size, and the list lost its cache when it got pages.
A size with a limit: clients will pick the size with ?size=, but only up to a limit. Without one, a single request for ?size=1000000 reads and sends the whole table, and the pages were for nothing. Read the number with a default, and refuse anything out of range with 400, like this handler for reviews does:
app.HandleFunc("GET /reviews", func(w http.ResponseWriter, r *http.Request) {
count, err := strconv.Atoi(r.URL.Query().Get("count"))
if err != nil {
count = 10
}
if count < 1 || count > 20 {
writeJSON(w, 400, map[string]string{"error": "count must be between 1 and 20"})
return
}
// ...
})
One cache entry per page: a single key like "products" would hand out the first cached page for every page. Each page needs its own cache key, built from everything that decides its content: after and size, as in "products:6:3". The handlers that change products already call clear(cache), which forgets every page at once.
On a fresh shop, these requests, in this order, must get these answers:
GET /products → 200 {"next_after": 3, "products": [{"id": 1, "name": "Mug", "price": 12}, …]}
GET /products?size=5 → 200 {"next_after": 5, "products": [… 5 products, ids 1 to 5 …]}
GET /products?after=6 → 200 {"next_after": null, "products": [{"id": 7, "name": "Stapler", …}, {"id": 8, …}]}
GET /products?after=6 → 200 the same page, from the cache
GET /debug/queries → 200 {"queries": 3}
POST /products {"name": "Cup", "price": 8} → 201 {"id": 9, "name": "Cup", "price": 8}
GET /products?after=6 → 200 {"next_after": 9, "products": [{"id": 7, …}, {"id": 8, …}, {"id": 9, "name": "Cup", "price": 8}]}
GET /products?size=6 → 400 {"error": "size must be between 1 and 5"}
GET /products?size=0 → 400 {"error": "size must be between 1 and 5"}
GET /debug/queries → 200 {"queries": 5}
Your task: add const maxPageSize = 5. Read size with strconv.Atoi (the given if err != nil falls back to pageSize), and answer 400 when it is below 1 or above 5. The page query already uses size as its LIMIT: keep the answer in a variable, send it with writeJSONWithETag, and cache each page under its own key for cacheSeconds.
Assignment
Follow the Coding Tutorial to add a page size limit to GET /products and give each page its own cache entry.
Hint
Build the key from both after and size, with strconv.Itoa(...) around the numbers. Check the size before you make the key, so a refused size never runs a query or fills a cache entry. And return after sending a cached page, or the handler runs its query anyway.
Free plan: 30 tutor messages. PRO is unlimited.
