A site that pre-renders or caches its pages serves them fast, but freezes the data into the build – edit a price in the admin and the built page keeps showing the old one until something tells it to refresh. A small endpoint does the refreshing, and Fontdue calls it whenever your content changes.
The revalidate endpoint
fontdue-js ships the route handler. Re-export its POST from an API route and you’re done – calling it clears every cached response, so the next request to any page re-fetches fresh data:
A small API route does the refreshing. It checks a shared token, then purges the CDN cache so the next request re-fetches fresh data. The token keeps anyone who finds the URL from triggering it:
// src/app/api/revalidate/route.ts
export { POST } from "fontdue-js/next/revalidate";
// app/routes/api.revalidate.ts
import type { Route } from "./+types/api.revalidate";
import { purgeCache } from "@netlify/functions";
export async function action({ request }: Route.ActionArgs) {
const expected = process.env.REVALIDATE_TOKEN;
if (!expected) return new Response("REVALIDATE_TOKEN not configured", { status: 500 });
if (new URL(request.url).searchParams.get("token") !== expected) {
return new Response("Unauthorized", { status: 401 });
}
await purgeCache({ tags: ["fontdue"] });
return Response.json({ purged: ["fontdue"] });
}
// src/routes/api.revalidate.ts
import { createFileRoute } from "@tanstack/react-router";
import { purgeCache } from "@netlify/functions";
export const Route = createFileRoute("/api/revalidate")({
server: {
handlers: {
POST: async ({ request }) => {
const expected = process.env.REVALIDATE_TOKEN;
if (!expected) return new Response("REVALIDATE_TOKEN not configured", { status: 500 });
if (new URL(request.url).searchParams.get("token") !== expected) {
return new Response("Unauthorized", { status: 401 });
}
await purgeCache({ tags: ["fontdue"] });
return Response.json({ purged: ["fontdue"] });
},
},
},
});
// src/pages/api/revalidate.ts
import type { APIRoute } from "astro";
import { purgeCache } from "@netlify/functions";
export const POST: APIRoute = async ({ request }) => {
const expected = import.meta.env.REVALIDATE_TOKEN;
if (!expected) return new Response("REVALIDATE_TOKEN not configured", { status: 500 });
if (new URL(request.url).searchParams.get("token") !== expected) {
return new Response("Unauthorized", { status: 401 });
}
await purgeCache({ tags: ["fontdue"] });
return Response.json({ purged: ["fontdue"] });
};
The cache tags are set for you: withFontdue and the fetch helper tag every request, and the re-exported handler clears them when called. There’s nothing else to wire.
The loaders tag their responses fontdue and set CDN cache headers so the edge serves a cached copy and revalidates in the background; the purge above clears that tag. The exact headers live in your template’s root layout or middleware.
Trigger it from Fontdue
Point Fontdue at this endpoint so it fires whenever your content changes. In the admin, open Settings → Integration, find the Deploy hook URL field, and enter your endpoint:
https://your-site.netlify.app/api/revalidate
https://your-site.netlify.app/api/revalidate?token=YOUR_REVALIDATE_TOKEN
https://your-site.netlify.app/api/revalidate?token=YOUR_REVALIDATE_TOKEN
https://your-site.netlify.app/api/revalidate?token=YOUR_REVALIDATE_TOKEN
Fontdue posts to that URL after a change publishes – a new collection, an edited price, an updated content page – and the site picks up the change without a full redeploy.11The hook is debounced on the Fontdue side, so a burst of edits collapses into a single call rather than one per save.