Careeroza
On-demand Revalidation
revalidatePath and revalidateTag
When an admin edits a product, you do not want to wait for the timer. Clear the cache right away from a Server Action or Route Handler:
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
import { prisma } from '@/lib/prisma';
export async function updatePrice(id: number, price: number) {
await prisma.product.update({ where: { id }, data: { price } });
revalidateTag('product-' + id); // every fetch tagged with this
revalidatePath('/products/' + id); // this page
revalidatePath('/products'); // the listing
}Note: In Next.js 16, revalidateTag(tag, 'max') takes a cache-life profile as a second argument for stale-while-revalidate behaviour, and the new updateTag(tag) (Server Actions only) expires the tag immediately so the user sees their own change.
A Webhook From a CMS
app/api/revalidate/route.ts — a headless CMS or your NestJS backend calls this after content changes:
import { revalidateTag } from 'next/cache';
import { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
if (req.headers.get('x-revalidate-secret') !== process.env.REVALIDATE_SECRET) {
return Response.json({ message: 'Invalid secret' }, { status: 401 });
}
const { tag } = (await req.json()) as { tag: string };
revalidateTag(tag);
return Response.json({ revalidated: true, tag });
}Example
curl -X POST localhost:3000/api/revalidate -H "x-revalidate-secret: s3cr3t" -H "Content-Type: application/json" -d '{"tag":"products"}'Example output
{"revalidated":true,"tag":"products"}Tags in TypeScript
Example
A cache where each entry has tags, and invalidating a tag removes every entry that has it:
class TaggedCache {
private entries = new Map<string, { value: string; tags: string[] }>();
set(key: string, value: string, tags: string[]): void {
this.entries.set(key, { value, tags });
}
get(key: string): string {
return this.entries.get(key)?.value ?? '(miss)';
}
revalidateTag(tag: string): void {
for (const [key, e] of this.entries) if (e.tags.includes(tag)) this.entries.delete(key);
}
}
const c = new TaggedCache();
c.set('/api/products/1', 'Redmi ₹17999', ['products', 'product-1']);
c.set('/api/products/2', 'boAt ₹1299', ['products', 'product-2']);
c.set('/api/categories', 'Mobiles, Audio', ['categories']);
c.revalidateTag('product-1');
console.log(c.get('/api/products/1'), '|', c.get('/api/products/2'), '|', c.get('/api/categories'));
c.revalidateTag('products');
console.log(c.get('/api/products/2'), '|', c.get('/api/categories'));Output
(miss) | boAt ₹1299 | Mobiles, Audio (miss) | Mobiles, Audio