We've seen how to use Incremental
Static Regeneration,
by setting the "revalidate" option,
which means this page will expire
after a certain timeout,
and therefore be regenerated, fetching new data.
However even this approach has some downsides.
When we modify a product in our CMS, our pages
won't update immediately, but only after
the revalidate period expires.
Also, it's not easy to tell exactly
when a page will be updated,
because it's only when somebody actually requests
that page that Next.js checks if it's expired,
and if so regenerates it.
Now, Next.js 12.2 introduced a new feature called
"on-demand revalidation" that
doesn't have those downsides.
What this allows us to do is to regenerate a page
as soon as we modify some data in the CMS.
Let's take a quick look at
the example on this page.
The way this works is that we can expose
an API route in our Next.js app,
that will be called by our CMS
whenever we update some data.
And in this API handler we can tell Next.js
which page needs to be regenerated,
based on which data was updated in the CMS.
Now, to use this approach, we need a CMS
capable of calling our Next.js app.
Most content management systems will
provide some way to do that.
In Strapi, if we go to Settings,
there is a section called "Webhooks".
A "webhook" is simply a URL we can call to notify
another application when something happens.
Let's go ahead an create a new webhook,
that we can call "Revalidate". The
name is not really important.
As the URL we want to call our Next.js app, that, when running locally,
is at localhost port 3000.
We'll need to create an API route, so
it will be under the "/api" path.
And we can call it "revalidate". Again,
this is an arbitrary choice.
The other thing we need to do is select which
"Events" will trigger a call to the webhook.
If we select "Entry", this means it will be called any time we modify any entry,
like a product.
We can leave Create, Update, and Delete all selected,
so we'll be notified of all the changes.
Ok, we have our webhook configured,
so let's save this.
Now, let's go back to our Next.js app,
and see what we need to do there.
First of all, let me restart the Next.js server in
dev mode, so it will be easier
to test our new API handler.
But before we get to that, with on-demand
revalidation we don't need this
"revalidate" timeout any more.
Because the page will be regenerated when we tell Next.js to do that,
not based on a timeout.
And in fact we can remove it
from the home page as well.
Now, we said we need an API route, so let's
re-add the "api" folder under pages.
And create a new file here, that we need to call
"revalidate.js", to match the
URL we configured in Strapi.
So here we need a handler function,
let's call it "handleRevalidate",
that accepts request and response objects,
and it should be the default export for this file.
Now, Strapi will post a JSON
object to this API route,
so let's start by logging the
data we receive from Strapi,
that will be in the request body.
We still need to return some response, so let's return a 204,
which is No Content.
Strapi only needs a successful response,
no need to return any data.
Ok, so let's keep an eye on the server logs,
and go and modify some data in Strapi,
to see what happens.
I'll just change the first product
to say "Aloe Vera 2",
and when we save, Strapi will
call our new API route,
sending an object with lots of
details about what's changed.
There's an "event" property, that
describes the event type.
In this case it's "entry.update", because
we modified an existing product.
If we add a new product instead the
event will be "entry.create".
Another interesting field is the "model",
that's set to "product".
This tells us which Strapi collection we modified.
Then there's a nested "entry" object,
that contains all the product details,
including its "id".
And the "id" is in fact all
we need for our purposes,
so we can ignore the other properties.
What we want to do in our code
is look at that event object,
that I'm going to assign to its own variable,
just so it's clearer,
and first of all we want to check if
the event is about a "product",
because we're not interested in changes to other collections,
like cart items for example.
If it is a Product then we can get
its "id" from the "entry" object.
And with this we know which pages to regenerate.
So here we can call "res.revalidate", passing the
path of the page that needs to be regenerated.
If we go to our website, we display
each product on the home page,
(By the way, it's showing the new data now because
we're running in dev mode, so it regenerates
the page at every request.)
But we also need to update the product details page,
that's at this path.
So in our code we want to revalidate the home page,
that's at the root path,
and "revalidate" is async, so we should await it.
Then we also want to update the product page,
but for the "id" that we received from Strapi.
Note how we can revalidate multiple pages
in response to a single data change.
Now, with this code we're doing
the revalidation in sequence:
first the home page, and only when that's
finished we process the product page.
It's probably better to revalidate both pages in parallel,
and we can do that using Promise.all.
This takes an array with multiple promises, and will
resolve when all the promises have completed.
So we can pass both calls to revalidate inside the array,
and they will effectively run in parallel.
Ok. When it's done, we could log
something to the console,
so we can tell if everything worked,
and which product was updated.
We can remove the other log at this point.
Ok, let's see what happens now, if we go
and modify a product in Strapi again.
If we look at the Next.js logs, you can see that
it did revalidate the product with "id: 1".
So there are no errors at least.
But to fully test this functionality we need to run the app in production mode,
so let's do a build.
Now we can run "npm start".
Ok, let's go back to the browser,
and start from the home page.
We currently have "Aloe Vera 3",
Now let's change it to "4",
and at this point we should see the
"revalidated" message in the logs.
Which means Next.js should have
regenerated this page,
and in fact the page shows "Aloe Vera 4".
Same thing if we go back to the home page:
the data has been updated here as well.
Let's try testing another product.
I'll change the "Snake Plant" this time.
And straight away, as soon as we reload the page,
it's showing the new product title.
Note how with on-demand revalidation
the new page is immediately visible,
because it gets regenerated as soon as we
call "res.revalidate" in the API handler.
If you remember, when setting a "revalidate" timeout,
like in the previous video,
the first request after the timeout triggers the regeneration,
but still shows the old data.
So this is another advantage
of on-demand revalidation.
If you're using a CMS that supports
calling a webhook or similar,
I recommend using on-demand revalidation,
rather than a revalidate timeout.
Note that this API handler works with Strapi,
if you use some other CMS the webhook request may be a bit different,
and contain different data.
So you'll need to check the documentation for that
specific CMS to find out exactly how it works.
Finally, I should mention that this video was added
after the rest of the course was recorded,
because on-demand revalidation was
only added in Next.js 12.2.
So in the next video you'll see the code as
it was at the end of the previous video,
that is using a revalidate timeout,
not on-demand revalidation.
If you're following along with the code, please revert
back to the 30-second revalidate timeout,
and disable the Strapi webhook.
But all the concepts in the following lectures
also apply to on-demand revalidation.
And that includes how to set a "fallback" option
to allow new products to be displayed,
that is what the next video is about.