Measure first: the old GET /customers/1/orders ran one query for the customer's orders, then one more per order for that order's items. Customer 1 has 2 orders, so that was 3 queries; a customer with 50 orders would cost 51. This is the N+1 problem, and it hides in any loop that runs a query.
A JOIN brings rows of two tables together in one query. For example, a route showing one order's items could get each item's quantity with its product's name in a single query:
var query = "SELECT order_items.quantity, products.name ";
query += "FROM order_items ";
query += "JOIN products ";
query += "ON products.id = order_items.product_id ";
query += "WHERE order_items.order_id = $orderId ";
var cmd = RunQuery(query);
cmd.Parameters.AddWithValue("$orderId", orderId);
using var rows = cmd.ExecuteReader();
Each += adds a piece to the end of query, so each piece ends with a space. Each row of the orders JOIN is one item, with its order's columns next to it, so an order with two items comes as two rows. A dictionary from order id to that order's list of items groups them back into orders.
Your code must give the same answers as before, with one query per request:
GET /customers/1/orders → 200 [{"id": 1, "items": [{"product_id": 1, "quantity": 2}, {"product_id": 4, "quantity": 5}], "ordered": "2026-03-02", "status": "paid"}, {"id": 3, "items": [{"product_id": 2, "quantity": 3}, {"product_id": 4, "quantity": 10}], "ordered": "2026-04-05", "status": "paid"}]
GET /debug/queries → 200 {"queries": 1}
GET /customers/3/orders → 200 [{"id": 4, "items": [{"product_id": 5, "quantity": 1}, {"product_id": 1, "quantity": 1}], "ordered": "2026-04-18", "status": "shipped"}, {"id": 7, "items": [{"product_id": 1, "quantity": 4}, {"product_id": 2, "quantity": 2}], "ordered": "2026-05-30", "status": "paid"}]
GET /debug/queries → 200 {"queries": 2}
GET /customers/4/orders → 200 [{"id": 5, "items": [{"product_id": 6, "quantity": 2}, {"product_id": 3, "quantity": 1}], "ordered": "2026-05-07", "status": "paid"}]
GET /customers/5/orders → 200 []
GET /debug/queries → 200 {"queries": 4}
Your task: in the /customers/{id}/orders route, build one query that joins orders and order_items for this customer, sorted by order id. Run it once, then group its rows into orders with a dictionary itemLists.
Assignment
Follow the Coding Tutorial to replace the query-per-order loop with one JOIN and group its rows in C#.
Hint
An order with two items comes as two rows with the same rows["id"]. Make a new list only when that id isn't in itemLists yet, add the order to result with that same list as its items, and add every row's item to itemLists[rows["id"]].
Free plan: 30 tutor messages. PRO is unlimited.
