Instant revalidation
ISR refreshes on a clock. To make a publish go live in seconds instead, pair createGhostwritr({ tags }) with createRevalidateHandler — a ready-made route handler that verifies Ghost Writr’s signed feed.updated webhook and busts your caches the moment the engine rebuilds your feed.
See Instant updates for the concept; this page is the Next.js wiring.
-
Tag your feed fetches. Add
tagsto the client so the fetches it makes are revalidatable on demand. Keeprevalidateas your fallback window.lib/ghostwritr.ts import "server-only";import { createGhostwritr } from "@ghostwritr/next/server";export const gw = createGhostwritr({siteId: process.env.GHOSTWRITR_SITE_ID!,revalidate: 3600,tags: ["ghostwritr"], // every feed fetch is tagged}); -
Add the webhook route.
createRevalidateHandlerlives at the@ghostwritr/next/revalidatesubpath (it importsnext/cache, so it’s server-only). Export it as thePOSTof a route handler:app/api/revalidate/route.ts import { createRevalidateHandler } from "@ghostwritr/next/revalidate";export const POST = createRevalidateHandler({secret: process.env.GHOSTWRITR_FEED_SECRET!,tags: ["ghostwritr"], // MUST match the tags you set on createGhostwritr}); -
Set the secret.
GHOSTWRITR_FEED_SECRETis the shared secret from this site’s feed subscription — the engine signs the webhook body with it. It is distinct from yoursiteId:siteIdis the public read capability;GHOSTWRITR_FEED_SECRETis the private signing key..env.local GHOSTWRITR_SITE_ID=your-site-idGHOSTWRITR_FEED_SECRET=your-feed-secret -
Register the URL. Point your site’s feed subscription at the deployed route, e.g.
https://yoursite.com/api/revalidate. On the next publish the engine POSTs a signedfeed.updatedbody and your pages refresh in seconds.
How verification works
Section titled “How verification works”The handler reads the raw request body once and verifies the X-GW-Signature HMAC-SHA256 over those exact bytes before it revalidates anything. A request with a missing, forged, or tampered signature is rejected with 401 and never touches the cache. Only after a valid signature does it call revalidateTag() for each tag and revalidatePath() for each path, then returns { "revalidated": true }.
Revalidating concrete paths
Section titled “Revalidating concrete paths”Tags cover any page that fetched the feed through the tagged client. For pages rendered without a tagged fetch — a hand-built sitemap.xml, a route that lists slugs from a separate call — add their paths:
import { createRevalidateHandler } from "@ghostwritr/next/revalidate";
export const POST = createRevalidateHandler({ secret: process.env.GHOSTWRITR_FEED_SECRET!, tags: ["ghostwritr", "blog"], paths: ["/blog", "/sitemap.xml"],});paths defaults to []. Add your sitemap and any index pages so they refresh alongside the articles — see SEO metadata & sitemap.
Hand-rolling the route
Section titled “Hand-rolling the route”If you’d rather build the handler yourself, verify the signature with the primitives from @ghostwritr/feed:
import { revalidateTag } from "next/cache";import { FEED_SIGNATURE_HEADER, verifyFeedSignature } from "@ghostwritr/feed";
export async function POST(req: Request) { const rawBody = await req.text(); const signature = req.headers.get(FEED_SIGNATURE_HEADER); const ok = await verifyFeedSignature(process.env.GHOSTWRITR_FEED_SECRET!, rawBody, signature); if (!ok) return new Response("invalid signature", { status: 401 }); revalidateTag("ghostwritr"); return Response.json({ revalidated: true });}Always verify over the raw body before parsing — the signature is over those exact bytes.
What to reach for next
Section titled “What to reach for next”- Rendering modes — how
revalidateandtagsinteract. - Error handling — what the reads throw when a feed is missing.
- Instant updates — the end-to-end publish-to-live flow.