We wrote a route handler,
so we can receive notifications from Strapi,
whenever the data in the CMS is modified.
And we tested it using the Trigger
button on this page.
But let's see how it works
if we make a real change.
For example, we could delete
this "subnautica-2" entry,
that we created in one of our previous tests.
Let's go and see what was sent to our webhook.
The notification includes all the entry fields.
But it also has an "event" property,
that tells us what type of change occurred.
In this case it's "entry.delete".
Another useful property is "model",
that specifies which collection type was modified,
in case we have different object types in the CMS.
Now, let me clear the logs,
and try a different type of change.
Let's see what happens when we create a new entry.
I'll duplicate this one,
and change it to "celeste-2",
both in the slug and in the title.
Let's see what kind of notification
we get in this case.
The "event" is now "entry.create".
And again we get all the fields.
But note that "publishedAt" is "null",
because we haven't actually published the entry.
It's still a "draft version".
Let's go and publish it now.
And see what kind of event we get this time.
There is one saying "entry.update",
because publishing a review also updates
its record in the database I suppose.
But then we also receive another notification,
with "entry.publish" as the event.
So we get two notifications:
"update" and "publish".
Now, we could write some checks in our code
to make sure we only regenerate our pages
when the review is actually published,
not when it's still in draft.
And also to only revalidate once
even if we receive two different
notifications like in this case.
But, to keep things simple,
I'm only going to check if the
change is about a "review".
Even if we revalidate when it's
not strictly necessary
that's not really a big deal.
So in our handler let's check the "payload.model"
and only do something if it's a "review".
Now, here we want to trigger the revalidation.
Let's take another look at
the Next.js documentation.
In our route handler we can call
the "revalidateTag" function,
passing a "tag" that identifies
a certain "fetch" request.
This will effectively invalidate the
data cached by that "fetch" call.
So, in our code we need to start from
where we make the "fetch" request.
Instead of setting a "revalidate" interval
we want to pass some "tags",
that's an array of strings.
Now, a "tag" is just an arbitrary
value we can choose
to identify one or more fetch requests.
So I'll use "reviews" for this one.
But since we'll need to use exactly the same value
when triggering the revalidation,
it's better if we define a constant,
and export it.
Let's call it "CACHE_TAG_REVIEWS".
This way we'll be able to import
it into the route handler.
But first let's use it in this file,
passing it in the "tags" array.
Note that, by doing this here,
the tag will apply to all CMS requests.
Because this "fetchReviews" function
is the shared code,
called by both "getReview" and "getReviews" ,
with different parameters.
So this tag will be applied to
both the individual reviews,
and the lists displayed in the
Home and Reviews pages.
This is what we want in this application,
because when we modify an entry in the CMS
we want to update both the page for that review,
and all the pages were that entry is listed.
Let's go to our route handler now.
Here we want to call "revalidateTag",
that's a function provided by
the "next/cache" module.
And pass it our custom tag
that we exported from our "lib/reviews" module.
Again, this will invalidate any data in the cache
stored from "fetch" requests
labelled with this tag.
Let's log something in this case.
We could print "revalidated"
followed by the tag value.
Ok, let's save our code,
and test this functionality.
We need to do a production build
because we cannot test revalidation
in the dev server.
Note that there's a new route for the webhook,
and it's shown as dynamic,
because the route handler will
be executed for every request.
But let's start the server now
and go and test our application.
Let me put the browser on the right,
and start from the Home page.
All pages have been statically generated,
so when we load them the first time
the server doesn't need to rerender anything.
But let's see what happens if we go
and change something in the CMS.
I'll just modify this existing review,
and add "update 1" to the title.
If we go back to our Next.js app,
you can see in the logs that
our webhook was called,
and revalidated all fetch requests
tagged with "reviews".
So, if we reload the Home page,
we see a bunch of "rendering"
messages in the logs.
Because the server detected that
the data in the cache was no longer valid,
and therefore these pages had to be regenerated.
But note that the re-rendering only happened
when we requested a page in the browser.
Simply calling "revalidateTag"
in our route handler
doesn't cause the pages to be
regenerated immediately.
It only marks the data in the cache as invalid.
In fact you can also see that
the page in the browser still shows the old title.
That's because, just like with
time-based revalidation,
the first request still receives the old page.
The new page will be returned if we reload again.
You can see that it shows the updated title now.
At this point all other pages
should be up to date as well.
Because all fetch requests are
labelled with the same tag,
so by calling "revalidateTag"
we invalidate all the cached CMS data.
And therefore Next.js regenerates all the pages
that were using that data.
And this is how on-demand revalidation works.
We expose a webhook that receives
notifications from the CMS,
and then we call revalidateTag,
passing the same tag we specify when
making the "fetch" requests.
This way we can make sure that
any data in the cache is invalidated
as soon as we modify it in the CMS.