-
Notifications
You must be signed in to change notification settings - Fork 13
Description
A clear and concise description of what the feature is
Add support for configurable fetchOptions in the @strapi/client to allow developers to control caching behavior when using the client in Next.js applications (App Router).
Currently, @strapi/client doesn't expose any API to configure fetch options, which prevents developers from leveraging Next.js's built-in caching mechanisms (ISR, cache tags, on-demand revalidation, etc.).
Why should this feature be included?
Problem:
- Next.js App Router aggressively caches
fetchrequests by default - The Strapi client provides no way to configure this caching behavior
- This results in stale data being served even after calling Next.js revalidation APIs
- Developers are forced to choose between completely disabling caching (
force-dynamic) or dealing with stale data
Impact on developers:
-
Performance Issues: Forces developers to sacrifice performance by completely disabling caching with
export const dynamic = 'force-dynamic', which eliminates the benefits of static generation and ISR -
Poor Developer Experience: Requires complex workarounds like wrapping every Strapi call with
unstable_cache, which doesn't integrate well with Strapi's API and adds unnecessary boilerplate -
SEO Challenges: Makes it difficult to implement proper ISR (Incremental Static Regeneration) for content-heavy sites that need both performance and fresh data
-
Scalability Concerns: Prevents efficient caching strategies, leading to unnecessary API calls to Strapi backend
Current workarounds attempted:
❌ Page-level force-dynamic - Disables all caching, poor performance
❌ unstable_cache wrapper - Adds complexity, doesn't integrate well
❌ revalidatePath/revalidateTag - Inconsistent because underlying requests aren't properly tagged
Why this matters:
- Next.js 13+ App Router has fundamentally changed how caching works
- Strapi is a popular headless CMS choice for Next.js projects
- This gap between the two tools creates a significant pain point for the community
Please provide an example for how this would work
Option 1: Global fetchOptions configuration (Recommended)
import { strapi } from '@strapi/client';
// Initialize client with fetch options
export const client = strapi({
baseURL: process.env.STRAPI_BASE_URL,
auth: process.env.STRAPI_AUTH_TOKEN,
fetchOptions: {
cache: 'no-store', // Disable caching globally
next: {
revalidate: 60, // Or enable ISR with 60s revalidation
tags: ['strapi-data'] // Add cache tags for on-demand revalidation
}
}
});
// Usage remains the same
const collection = await client.collection('pages');
const { data } = await collection.findOne('page-id');Option 2: Per-request fetchOptions override
// Global default settings
export const client = strapi({
baseURL: process.env.STRAPI_BASE_URL,
auth: process.env.STRAPI_AUTH_TOKEN,
fetchOptions: {
next: { revalidate: 3600 } // Default: 1 hour cache
}
});
// Override per request
const collection = await client.collection('pages');
const { data } = await collection.findOne('page-id', {
populate: '*',
fetchOptions: {
next: {
tags: ['specific-page'], // More specific cache tag
revalidate: 60 // Different revalidation time
}
}
});Option 3: Callback-based configuration
export const client = strapi({
baseURL: process.env.STRAPI_BASE_URL,
auth: process.env.STRAPI_AUTH_TOKEN,
configureFetch: (url, options) => {
// Add cache tags based on URL
const tags = [];
if (url.includes('/pages')) tags.push('strapi-pages');
if (url.includes('/articles')) tags.push('strapi-articles');
return {
...options,
next: {
tags,
revalidate: 60
}
};
}
});Real-world usage scenario
// src/lib/strapi.ts
export const client = strapi({
baseURL: process.env.STRAPI_BASE_URL,
auth: process.env.STRAPI_AUTH_TOKEN,
fetchOptions: {
next: {
revalidate: 60,
tags: ['strapi']
}
}
});
// src/app/blog/[slug]/page.tsx
export default async function BlogPost({ params }) {
const collection = await client.collection('articles');
const { data } = await collection.findOne(params.slug, {
populate: '*',
fetchOptions: {
next: { tags: [`article-${params.slug}`] }
}
});
return <article>{/* render article */}</article>;
}
// src/app/api/revalidate/route.ts
export async function POST(request) {
const { slug } = await request.json();
// Now this actually works!
revalidateTag(`article-${slug}`);
return Response.json({ revalidated: true });
}Additional Context
Environment
@strapi/client: ^1.5.0next: 14.2.23react: 18.2.21- Node.js: v18+
Additional Context
This issue is particularly relevant for Next.js 13+ App Router users, as the framework's caching behavior has changed significantly. Many developers building headless CMS applications with Strapi + Next.js would benefit from this feature.
Related discussions:
Would the maintainers be open to a pull request implementing one of these solutions? I'm happy to contribute if there's interest in this feature.