A demo application showcasing how Next.js on-demand incrementation static regeneration can be used in combination with Kontent.ai webhooks to revalidate pages when content changes are published.
This project is based on the official Next.js cms-kontent
example.
A full write up of on-demand ISR and the considerations for real world sites can be found on the Kontent.ai blog: How to improve cache efficiency and reduce costs with Next.js on-demand ISR.
- Node.js
- Yarn
- A Kontent.ai account.
- An ngrok account (if working locally).
git clone git@github.com:tommarshall/next-kontent-on-demand-isr-demo.git
cd next-kontent-on-demand-isr-demo/
Scaffold the Next.js environment configuration file.
cp .env.local.example .env.local
Create a new, empty project in Kontent via the web UI.
Then set the following variables in .env.local
:
KONTENT_PROJECT_ID
- Should be the Project ID inProject settings
>API keys
.KONTENT_PREVIEW_API_KEY
- One of the Preview API keys inProject settings
>API keys
.KONTENT_PREVIEW_SECRET
- Can be any random string (but avoid spaces), likeNOT_A_SECRET
- this is used for Preview Mode.KONTENT_WEBHOOK_SECRET
- Leave blank, we'll set this later.
Configure the Preview URLs for the Post type, if you want to use it:
http://localhost:3000/api/preview?secret=NOT_A_SECRET&slug={URLslug}
Import the demo project into Kontent using kontent-cli
. You'll need to enable the Management API in the project settings to generate the API key.
yarn run kontent backup --action=restore --apiKey=<Management API key> --projectId=<Project ID> --zipFilename=kontent-backup
Then publish all the imported items via the Kontent web UI.
If working locally, use ngrok to create a public URL with a tunnelled connection to the Next.js port on your local machine.
ngrok http 3000
Create a new webhook in the project via Project settings
> Webhhooks
with the URL address set to /api/revalidate
api route with the ngrok origin, e.g.
https://5048-86-151-48-86.ngrok.io/api/revalidate
Then set the KONTENT_WEBHOOK_SECRET
variable in .env.local
using the webhook secret generated by Kontent.
Install the Next.js node modules via yarn.
yarn install
Start the Next.js client application in production mode.
yarn build
yarn start
You should now be able to see the example blog application in your browser at http://localhost:3000.
Important: We need to use
yarn build
andyarn start
rather thanyarn dev
to test the on-demand revalidation asyarn dev
callsgetStaticProps
on each request, regardless of the incremental static regenerationrevalidate
config.
Publish a change to any post in Kontent.
The ngrok logs in the terminal should show a 200 OK
from the webhook request.
Refresh the corresponding post page in your browser and you should see your change. Success! 🙌
Next.js' revalidate()
requires a URL path to revalidate a page.
Kontent's webhook requests do not contain any detail regarding the change, nor does the Delivery API allow us to ask for older versions of a content item.
So if we change the URL slug of content item in Kontent, the webhook handler API route generates the URL path using the new URL slug value, which causes revalidate()
to error, as it cannot find a page to revalidate for that new path.
If fallback: true
or fallback: blocking
is set for the page route then the page will be visible at the new path, but unless we set a revalidate
duration for thge page route the page will continue to serve from the old path as well.
Neither the pages/index.js
nor the pages/posts/[slug].js
getStaticProps
in this example declare a revalidate
duration. This means Next.js will use the default value of false
(no revalidation) and only revalidate the page on-demand, when revalidate()
is called.
Currently, the api/revalidate.js
endpoint will only revalidate the immediate post page when a post is changed. That's fine for this proof-of-concept, but for production we'd need to consider that:
- Posts appear on the homepage.
- Posts appear in the 'more stories' section on other post pages.
- Author content might also change.
To resolve this we'd either need to set a sensible revalidate
duration for both the home and post page routes, or implement additional logic within the api/revalidate.js
endpoint to handle these cases via revalidate()
.