Start learning free

LESSON 127 OF 173 · BACKEND: SPEED

Fix the N+1 in JavaScript

Practice

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:

const query = [
  "SELECT order_items.quantity, products.name",
  "FROM order_items",
  "JOIN products",
  "ON products.id = order_items.product_id",
  "WHERE order_items.order_id = ?",
].join(" ");
const rows = runQuery(query).all(orderId);

.join(" ") glues the array's strings into one, with a space between them, so each clause can sit on its own line. 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. An object that maps each order id to its entry 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 route for GET /customers/:customerId/orders, 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 an object entries.


Assignment
Follow the Coding Tutorial to replace the query-per-order loop with one JOIN and group its rows in JavaScript.


Hint
An order with two items comes as two rows with the same row.id. Make a new entry only when entries[row.id] is undefined, push that same entry to result, and push every row's item to entries[row.id].items.


STEP 1 OF 11
    Ctrl + Enter to check

    Speed N Plus One in JavaScript

    Keep going with JavaScript1,600+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free