Next.js on-demand ISR by Sanity GROQ-powered webhooks
Tags: nextjs, sanity, react, javascript
Published: 2022-02-18 00:29:27

With the latest Next.js 12.1 version finally we got one of the most powerful missing feature: [on-demand ISR](https://nextjs.org/blog/next-12-1#on-demand-incremental-static-regeneration-beta) 😮!

Thanks to this you can revalidate your SSG pages on the fly without rebuild all the site or without waiting the scheduled time set in the revalidate option as we were used to until today.

I love [Sanity](https://www.sanity.io/) as headless CMS for its user friendly studio and for the power of its tools and plugins; I used to install the [sanity-plugin-vercel-deploy](https://www.npmjs.com/package/sanity-plugin-vercel-deploy) plugin, very useful to update my SSG sites hosted on Vercel but this was meaning trigger a new build and redeploy all the entire site (I never used ISR with its revalidate option because, on bigger sites, the build cost would be too high).

One of the Sanity great feature is how they [manage webhooks](https://www.sanity.io/docs/webhooks): you can trigger an URL after you edit your data specifying which document type and what send as payload, simply querying your database with its GROQ query language!

Now you can add a new API URL in your Next.js web app to revalidate your page content on-demand and request it by the Sanity webhook trigger 🤩.

For example, in your blog, imagine to fix a post typo on the Sanity studio and, after less a second, see your edit live. Cool right? First of all you need to add a new API endpoint on your web app, adding a file like this on the `pages/api` folder (yes I 🥰 TypeScript too):

``` typescript
import { isValidRequest } from "@sanity/webhook"
import type { NextApiRequest, NextApiResponse } from "next"

type Data = {
  message: string
}

const secret = process.env.SANITY_WEBHOOK_SECRET

export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
  if (req.method !== "POST") {
    console.error("Must be a POST request")
    return res.status(401).json({ message: "Must be a POST request" })
  }

  if (!isValidRequest(req, secret)) {
    res.status(401).json({ message: "Invalid signature" })
    return
  }

  try {
    const {
      body: { type, slug },
    } = req

    switch (type) {
      case "post":
        await res.revalidate(`/news/${slug}`)
        return res.json({ message: `Revalidated "${type}" with slug "${slug}"` })
    }

    return res.json({ message: "No managed type" })
  } catch (err) {
    return res.status(500).send({ message: "Error revalidating" })
  }
}

```
In this function I accept a POST request with a `type` and a `slug` as payload; there are three main points to pay attention:

1. validate the [Sanity webhook secret](https://www.sanity.io/docs/webhooks#bfa1758643b3), so we are safe to accept the request
2. call the `revalidate` method passing the path that we need to purge as the argument
3. set the `useCdn` Sanity client option to `false` to allow to get fresh content after the revalidate call (the webhook is too fast 😅)

This is how I set my Sanity webhook:
![Sanity webhook settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywuyc13uvqz92l4k5mfr.png)

I chosen to send the document type on the payload so I can manage the revalidate with a unique endpoint but you are free to follow your best needs.

This new Next.js feature is the beginning of a new era:
- you don't need to SSR your pages but keep them on your CDN, with no power consumption 💚 and #JAMstack compliant
- updates will be immediately online, no more building wait time
- your editors will be happy to preview, publish and check contents on the fly!

Thanks Vercel 🔼!