Developers
This guide covers caching strategies for Agility CMS content, including Next.js cache tags, revalidation, and performance optimization.
This guide covers caching strategies for Agility CMS content, including Next.js cache tags, revalidation, and performance optimization.
Agility CMS content is cached at multiple levels:
Cache tags allow granular cache invalidation:
agilitySDK.config.fetchConfig = {
next: {
tags: [`agility-content-${contentID}-${languageCode}`],
revalidate: 60,
},
}
Content Items:
agility-content-{contentID}-{locale}
Content Lists:
agility-content-{referenceName}-{locale}
Pages:
agility-page-{pageID}-{locale}
Sitemaps:
agility-sitemap-flat-{locale}
agility-sitemap-nested-{locale}
Revalidate after a specific time:
agilitySDK.config.fetchConfig = {
next: {
tags: [`agility-content-${contentID}-${languageCode}`],
revalidate: 60, // Revalidate every 60 seconds
},
}
Revalidate via webhook:
// API route: /api/revalidate
import { revalidateTag } from 'next/cache'
export async function POST(request: Request) {
const { tag } = await request.json()
revalidateTag(tag)
return Response.json({ revalidated: true })
}
// lib/cms/getContentItem.ts
export const getContentItem = async <T>(params: ContentItemRequestParams) => {
const agilitySDK = await getAgilitySDK()
agilitySDK.config.fetchConfig = {
next: {
tags: [`agility-content-${params.contentID}-${params.languageCode}`],
revalidate: 60, // 60 seconds default
},
}
return await agilitySDK.getContentItem(params)
}
Override default revalidation:
const { fields } = await getContentItem<IPost>({
contentID: 204,
languageCode: "en-us",
// Custom revalidation (if supported by your wrapper)
})
Configure webhook in Agility CMS:
https://your-site.com/api/revalidate⚠️ There is no webhook "security key."
AGILITY_SECURITY_KEYis the preview key and is never sent with a webhook. Secure delivery signs each request instead — see Verifying Signed Webhooks.
// app/api/revalidate/route.ts
import { revalidateTag, revalidatePath } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'
import { Webhook } from 'standard-webhooks'
export async function POST(request: NextRequest) {
// Read the RAW body - re-serializing the JSON will break signature verification
const raw = await request.text()
// Verify the signature (only if secure delivery is enabled on the webhook)
const wh = new Webhook(process.env.AGILITY_WEBHOOK_SIGNING_SECRET!)
try {
wh.verify(raw, {
"webhook-id": request.headers.get("webhook-id")!,
"webhook-timestamp": request.headers.get("webhook-timestamp")!,
"webhook-signature": request.headers.get("webhook-signature")!,
})
} catch {
return NextResponse.json({ error: "Invalid signature" }, { status: 401 })
}
const body = JSON.parse(raw)
// Revalidate cache tags
if (body.contentID) {
revalidateTag(`agility-content-${body.contentID}-${body.languageCode}`)
}
// Revalidate paths
if (body.path) {
revalidatePath(body.path)
}
return NextResponse.json({ revalidated: true })
}
Delivery is at-least-once. Use the
webhook-idheader as an idempotency key if repeated revalidation would be a problem.
Pre-render pages at build time:
// app/[locale]/[...slug]/page.tsx
export async function generateStaticParams() {
const sitemap = await getSitemapFlat({
channelName: "website",
languageCode: "en-us"
})
return sitemap.map((page) => ({
slug: page.path.split('/').filter(Boolean),
}))
}
Update pages on-demand:
export const revalidate = 60 // Revalidate every 60 seconds
Preview mode bypasses cache:
const isPreview = await draftMode().isEnabled
if (isPreview) {
// Fetch draft content (bypasses cache)
agilitySDK.config.fetchConfig = {
next: { revalidate: 0 }
}
}
Use specific cache tags for granular invalidation:
tags: [`agility-content-${contentID}-${locale}`]
Revalidate related content together:
// Revalidate all posts when one is updated
revalidateTag('agility-content-posts-en-us')
Track cache hit rates and adjust strategies:
// Log cache misses
console.log('Cache miss for:', contentID)
Problem: Content not updating after changes
Solutions:
Problem: Cache not being used
Solutions:
Next: Preview Mode - Preview functionality