Build-Time Request Cache
Reduce redundant API calls during multi-locale Next.js builds with cachedFetch and createTenantCachedFetch.
This guide explains the build-time request cache feature for Next.js that reduces redundant API calls to content management systems and data services during multi-locale builds.
Problem
Multi-tenant, multi-locale Next.js apps fetch data from external APIs (Contentful, Sanity, Stripe, etc.) during build to pre-render pages. Without caching:
- Same endpoint fetched multiple times per locale
- 4 locales × 10 endpoints = 40 API calls on every build
- API rate limits exhausted quickly
- Longer build times
- Higher infrastructure costs
Solution
The @multitenant/next-app package provides cachedFetch() and createTenantCachedFetch() that automatically cache API responses during next build. On subsequent builds, cached data is reused, eliminating redundant API calls.
API Reference
cachedFetch<T>(url, options)
Simple cached fetch for a single request.
import { cachedFetch } from '@multitenant/next-app';
export async function generateStaticParams() {
const data = await cachedFetch<PageData>(
'https://api.example.com/pages',
{
locale: 'en-US',
debug: true,
},
);
return data.pages.map((p) => ({ slug: p.slug }));
}Parameters:
| Parameter | Type | Notes |
|---|---|---|
url | string | URL to fetch |
locale | string | Locale code (e.g., en-US, es-US) |
cacheDir | string | Optional: custom cache dir (default: .next/.build-cache) |
debug | boolean | Optional: log hits/misses to console |
log | function | Optional: custom log function |
createTenantCachedFetch<T>(registry, tenantKey, options?)
Creates a pre-configured cached fetch bound to a tenant's locale.
import { createTenantCachedFetch } from '@multitenant/next-app';
import { tenantRegistry } from '@/lib/tenant-registry';
const fetch = createTenantCachedFetch(tenantRegistry, 'us-main', { debug: true });
const data = await fetch<PageData>('https://api.example.com/pages');
// Override locale for this specific call
const esData = await fetch<PageData>('https://api.example.com/pages', { locale: 'es-US' });Features
- Build-phase only — no performance impact in
next devor runtime - Locale-scoped — different cached values per locale
- Async writes — non-blocking cache persistence
- Debug logging — understand what's cached
- Type-safe — full TypeScript support with generics
CLI Commands
Manage the cache from the command line:
# View cache statistics
multitenant cache --stats
# Invalidate specific locales
multitenant cache --locale en-US --locale es-US
# Invalidate all locales
multitenant cache --locale allExample: Contentful Integration
import { cachedFetch } from '@multitenant/next-app';
const CONTENTFUL_SPACE = process.env.CONTENTFUL_SPACE_ID!;
const CONTENTFUL_TOKEN = process.env.CONTENTFUL_ACCESS_TOKEN!;
export async function getPages(locale: string) {
return cachedFetch<ContentfulResponse>(
`https://cdn.contentful.com/spaces/${CONTENTFUL_SPACE}/entries?content_type=page`,
{
locale,
headers: {
Authorization: `Bearer ${CONTENTFUL_TOKEN}`,
},
},
);
}
export async function generateStaticParams() {
const pages = await getPages('en-US');
return pages.items.map((item) => ({
slug: item.fields.slug,
}));
}Cache Location
Cache files are stored in:
.next/.build-cache/
└── locales/
├── en-US/
│ ├── <sha256(url)>.json
│ └── ...
└── es-US/
├── <sha256(url)>.json
└── ...Each file contains the cached response, locale, URL, and timestamp.
Performance
With the cache enabled:
- First build: Normal speed (all URLs fetched)
- Subsequent builds: 60-80% faster (reuse cached responses)
- Per-locale: Each locale can have separate cache entries
- Per-URL: Cache is keyed by URL, not by component