Skip to content
All articles

The N+1 Problem Hiding in Your Static Build

1 min read
performancenext.jsdatabases

A thirty-page site took four minutes to build. The pages were trivial. The database was the problem.

What was happening

Each page called getSettings() for the site name in the footer. Thirty pages, thirty round trips — plus generateMetadata calling it again, plus sitemap.ts. Nearly a hundred reads for one document that could not change mid-build.

Why React's cache() did not help

cache() dedupes within a single render pass. Separate pages are separate passes. It is the right tool for one page calling the same loader from three components, and useless across pages.

A build-lifetime memo

const buildCache = new Map() function memoize(key, load) { if (typeof window !== 'undefined') return load() // browser must see fresh data const hit = buildCache.get(key) if (hit) return hit const pending = load() buildCache.set(key, pending) return pending }

Cache the promise, not the result — two pages starting the same load concurrently then share one request instead of racing.

The browser guard matters. The same module runs in the dashboard, where stale data is a bug.

Build dropped to forty seconds.