Batch the author lookups on the orders page
Node · Node · intermediate · modification
Replaces the N+1 author lookups with one `WHERE id = ANY($1)` query: collect the distinct author ids, fetch them in a single round-trip, and join in memory through a Map. Deleted authors come back as null per the contract, and an empty page short-circuits before the pool.
Requirements
- `attachAuthors(pool, orders)` decorates each order with its author record for the admin orders page; `orders` is up to 500 rows, each with a string `authorId`.
- Replace the per-order query pattern with a single database round-trip for the whole page — the endpoint's p95 is dominated by these lookups.
- Preserve the output contract: same orders in the same order, each with an added `author` field of `{ id, name, email }`, or `null` when the user row no longer exists (deleted accounts are expected).
- Orders may share an author; the author's id must be queried only once.
- An empty `orders` array must resolve to an empty array without touching the database.
- Queries go through the shared `pg` pool and must be parameterized.
Files touched
- src/orders/attachAuthors.js
--- src/orders/attachAuthors.js
export async function attachAuthors(pool, orders) {
- const result = [];
- for (const order of orders) {
- const { rows } = await pool.query(
- 'SELECT id, name, email FROM users WHERE id = $1',
- [order.authorId],
- );
- result.push({ ...order, author: rows[0] ?? null });
+ if (orders.length === 0) {
+ return [];
}
- return result;
+ const authorIds = [...new Set(orders.map((order) => order.authorId))];
+ const { rows } = await pool.query(
+ 'SELECT id, name, email FROM users WHERE id = ANY($1)',
+ [authorIds],
+ );
+ const authorsById = new Map(rows.map((row) => [row.id, row]));
+ return orders.map((order) => ({
+ ...order,
+ author: authorsById.get(order.authorId) ?? null,
+ }));
}