Taking one endpoint from 800ms to 80ms
Published June 27, 2026 · by Majd Ghithan, written with the help of AI
The order-details endpoint took 800 milliseconds. Not broken, just slow enough that the app felt heavy everywhere it was used. The team's instinct was "the server needs more memory." My instinct was: I have no idea why it's slow, and neither does anyone guessing.
So before touching anything, I profiled it. An hour later it returned in about 80 milliseconds, and I hadn't added a single server. I'd just found where the 800ms was actually going — which was nowhere anyone expected.
Here's the walk, because the method matters more than this one endpoint.
First: measure, don't guess
You cannot optimize what you haven't measured. The whole failure mode of performance work is a smart engineer optimizing the wrong thing confidently. So step one is always to break the request into where its time actually goes.
I installed Laravel Telescope for this — the Requests tab shows you, per request, the number of queries, the time in each, and how long the whole thing took. Clockwork does the same in a browser panel if you prefer. For a one-off, even a DB::listen() that logs every query plus a couple of timers around suspicious blocks is enough.
Here's what the profile showed for that 800ms:
Two things jumped out immediately, and neither was the server. Ninety-four queries for a single order. And a synchronous HTTP call to a shipping API, sitting right in the middle of the request, adding 140ms every time.
The 94 queries: a classic N+1
Ninety-four queries to show one order is a textbook N+1. The controller loaded the order, then looped its line items, and each iteration lazily loaded the product, the product's category, and the tax rule. One order with ~30 line items, three lazy relations each, and there's your ninety-something queries.
The fix is eager loading — tell Eloquent to fetch the relations up front in a handful of queries instead of one-per-row:
// Before: 1 + (30 × 3) = 91 queries
$order = Order::findOrFail($id);
// After: 4 queries total, regardless of line-item count
$order = Order::with([
'items.product.category',
'items.taxRule',
])->findOrFail($id);
Ninety-four queries collapsed to four. That alone took the DB time from 610ms to about 45ms — the single biggest win, and it cost me one with() call.
The 140ms external call: get it out of the request
The shipping API call was fetching a live delivery estimate. Fine feature, wrong place. The user's request was blocking for 140ms waiting on a third party we don't control — and if that API had a bad day, our endpoint would too.
Two options, depending on the product need. If the estimate can be slightly stale, cache it and refresh in the background — the request reads a cached value in under a millisecond. If it must be live, move it out of the critical path: load the page without it and fetch the estimate via a separate async call, so the main content isn't held hostage to a slow dependency.
// Was: blocking 140ms on every request
$estimate = $this->shipping->liveEstimate($order);
// Now: cached, refreshed out-of-band
$estimate = Cache::remember(
"ship:estimate:{$order->id}", 600,
fn () => $this->shipping->liveEstimate($order)
);
Cached, that 140ms became effectively zero on all but the occasional refresh.
The result
Eight hundred milliseconds to eighty. No new hardware, no framework tricks, no rewrite. Just one with() and one remember(), both aimed precisely at the two things the profile said were expensive.
That's the whole point. The team was ready to spend money on servers to fix a problem that was 91 unnecessary queries and one misplaced API call. If they'd added RAM, the endpoint would've stayed slow and they'd have paid for it monthly. Profiling turned a vague "it's slow, buy more" into two specific, cheap, correct fixes.
Every slow endpoint has a profile like this — a small number of things eating almost all the time, and a long tail that doesn't matter. Your job isn't to make everything faster. It's to measure, find the one or two bars that are tall, and go fix exactly those.
