Multitenant

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:

ParameterTypeNotes
urlstringURL to fetch
localestringLocale code (e.g., en-US, es-US)
cacheDirstringOptional: custom cache dir (default: .next/.build-cache)
debugbooleanOptional: log hits/misses to console
logfunctionOptional: 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 dev or 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 all

Example: 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

See Also

On this page