A dashboard page I worked on loaded a list of orders and felt heavy on every open. The endpoint was not doing anything clever. It was returning every order, every column, and every related record, then the frontend threw most of it away.
The page was not slow because the database was slow. It was slow because there was simply too much to move, parse, and render.
The wrong fix
The wrong fix is the one teams reach for first: add caching, buy a bigger server, blame the database.
All three hide the real problem. Caching a bloated payload still sends a bloated payload on the first load, and it goes stale the moment an order changes. A bigger server moves the same waste faster instead of removing it. And the database was never the bottleneck. It answered the question it was asked. The question was just far bigger than the screen needed.
The client asked for a warehouse and only needed a shelf.
The fix: paginate, and select only what the screen shows
Two changes, both boring on purpose.
First, paginate. Never return a whole table in one response. The screen shows twenty rows, so send twenty rows, plus a way to ask for the next twenty.
Second, select only the fields the screen actually shows. If the list shows an id, a total, and a status, those are the three fields the query should return. Not the full order record, not the customer record, not the line items. The detail view can fetch the rest when the user actually opens it.
const page = await db.orders.findMany({ take: pageSize, skip: pageSize * pageIndex, select: { id: true, total: true, status: true }, }); Twenty rows, three fields, one query. The page loads fast because there is simply less to move, parse, and render.
A few details that keep this honest
Cap the page size on the server. If the client can ask for ten thousand rows, pagination is only a suggestion. Pick a sane default, pick a hard maximum, and clamp anything above it.
Validate the page inputs. A negative page index or a missing page size should get a clear error, not a strange result set.
Keep the sort stable. Paging through a list that reorders itself between requests will show some rows twice and skip others. Sort by something unique, like the creation time plus the id, so page two starts exactly where page one ended.
And measure before you scale. Open the network tab, look at the response for your heaviest list, and count what the screen actually uses. That gap between what the endpoint returns and what the screen shows is the work you are paying for on every single load.
The habit behind the fix
Most slow endpoints I review are not slow. They are generous. They return everything just in case, and every layer downstream pays for that generosity: the database reads more, the network carries more, the client parses more, and the list renders more.
Before you scale anything, check what your endpoint actually returns. The cheapest performance win is usually not a new tool. It is a smaller answer.
What is the most wasteful payload you have shipped? Drop it in the comments.
I'm Sohail Khan, a Senior Full Stack Developer building AI agents and automation. Tech Lead at LoopxCorp. I write about backend engineering, AI integration and practical system design.


