Fikkie.
← All posts

Getting Laravel API responses under 200 ms

The three fixes that moved the needle most — indexes, eager loading, and caching — and how I measured each one.

Article

API slowness is rarely one bug. In my work at JMC Indonesia the backend had drifted into seconds-per-request territory, and it took a disciplined pass to get it down to consistent sub-200 ms responses. The three changes that moved the needle most were boring ones: indexes, eager loading, and caching.

Start with measurement

Before touching anything, I profiled the slow endpoints. Laravel’s query log plus a handful of explain calls showed me exactly where time went. The pattern that kept appearing was the same everywhere — N+1 relation loads and full table scans on hot columns.

1. Index the columns you actually query on

Indexes are the cheapest win in this list. I added composite indexes to the columns that appeared in where and order by clauses on the hottest tables. explain went from ALL to range and ref on the queries that mattered. No app code changed; the database just stopped scanning.

2. Replace N+1 with eager loading

The N+1 problem is the classic: a list endpoint fires one query per row to fetch a relation. Switching to eager loading collapsed dozens of queries into a couple. The rule I now apply is simple — if a list endpoint does more queries than it returns rows, it needs with().

3. Cache reads that don’t need to be live

Some endpoints were re-computing the same work on every hit. Caching read-heavy responses took the biggest single chunk off the profile. The key discipline was cache invalidation — I only cached data that changed rarely, and invalidated on write rather than guessing with TTLs.

The result

Consistent sub-200 ms responses across the mobile API. None of these were clever tricks. They were standard Laravel practice, applied carefully and verified with measurements after each change.