revalidatePath in Route Handlers
Answered
Plague posted this in #help-forum
PlagueOP
The documentation around revalidating data in Route Handler is confusing.
On one hand it says you can pass either the literally route segment (/path/1234) or the filesystem path (/path/[slug]/page), but, then in the "Revalidating A Page Path" section the syntax used is (/path/[slug], 'page') so which is it?
Do I need to actually specify the type of the revalidation (layout or page) do I need to include the page in the path (/path/[slug]/page), can I just pass in the path like actually specified in the route handler section of revalidation.
The documentation around caching is extremely confusing when on my end it seems like this data should be revalidated and it ends up not being revalidated, also the route handler section in "Routing" or in "File Conventions" say absolutely nothing about the fact that since Route Handlers are not tied to a specific route, revalidating data within them is not immediate like with Server Actions, you have to wait for the client-side route cache to invalidate (5 minutes for static routes, 30 seconds for dynamic routes).
My current situation using Next.js 14:
When content changes in my CMS, a webhook sends a request to my Route Handler (POST endpoint), which calls revalidatePath on a certain path. That is it.
What I expect: The data to be revalidated for a new visit, a hard refresh, or after 5 minutes (since my page is statically rendered).
What I see: Absolutely nothing. Nothing changes after a hard refresh, waiting 5 minutes, 30 minutes, an hour, even a whole day.
I've seen people have success with this exact same setup using revalidateTag instead, but, until
On one hand it says you can pass either the literally route segment (/path/1234) or the filesystem path (/path/[slug]/page), but, then in the "Revalidating A Page Path" section the syntax used is (/path/[slug], 'page') so which is it?
Do I need to actually specify the type of the revalidation (layout or page) do I need to include the page in the path (/path/[slug]/page), can I just pass in the path like actually specified in the route handler section of revalidation.
The documentation around caching is extremely confusing when on my end it seems like this data should be revalidated and it ends up not being revalidated, also the route handler section in "Routing" or in "File Conventions" say absolutely nothing about the fact that since Route Handlers are not tied to a specific route, revalidating data within them is not immediate like with Server Actions, you have to wait for the client-side route cache to invalidate (5 minutes for static routes, 30 seconds for dynamic routes).
My current situation using Next.js 14:
When content changes in my CMS, a webhook sends a request to my Route Handler (POST endpoint), which calls revalidatePath on a certain path. That is it.
What I expect: The data to be revalidated for a new visit, a hard refresh, or after 5 minutes (since my page is statically rendered).
What I see: Absolutely nothing. Nothing changes after a hard refresh, waiting 5 minutes, 30 minutes, an hour, even a whole day.
I've seen people have success with this exact same setup using revalidateTag instead, but, until
unstable_cache
becomes stable, I cannot tag my data since I fetching using the CMS client and not the fetch API.Answered by Plague
Never got this to work with Route Handlers, moved the api routes to pages and worked instantly as expected. App router caching is still a WIP clearly.
152 Replies
PlagueOP
I'd appreciate any clarification/help with this as this has been driving me insane the past few weeks.
(/path/1234)
(/path/[slug]/page)
(/path/[slug], 'page')
all work
(/path/[slug]/page)
(/path/[slug], 'page')
all work
have you tried to call the endpoint manually and does it revalidate?
PlagueOP
I have not tried calling it manually, not sure how that would differ from the webhook calling it?
no differ but to see if it work or not?
and have you checked does it actually hit the endpoint?
PlagueOP
Yeah it hits the endpoint, I get the success message, I see the log on vercel, the function runs, just doesn't seem to work.
can you show the code?
PlagueOP
import { headers } from "next/headers"
import { revalidatePath } from "next/cache"
export async function POST(request: Request) {
const data = await request.json()
const webhookSignature = headers().get('X-Contentful-Webhook-Name')
if (webhookSignature === process.env.CONTENTFUL_REVALIDATE_WEBHOOK) {
const spaceID = data["sys"]["space"]["sys"]["id"]
const contentTypeID = data["sys"]["contentType"]["sys"]["id"]
if (spaceID === process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID && contentTypeID === 'news') {
revalidatePath('/news/category/')
return Response.json({ revalidated: true, date: Date.now() }, { status: 201 })
}
}
return Response.json({ revalidated: false, date: Date.now() }, { status: 400 })
}
ignore all the validation lol, I am going to clean that up.
Here's a screenshot so it's easier to read:
have you tried
revalidatePath("/news/category")
PlagueOP
No, in the documentation they use single quotes, but, there shouldn't be a difference either way.
not the quote
but the slash after the path
PlagueOP
oh
/news/category
vs /news/category/
PlagueOP
I see
No, but, I did use
/news
before and it also didn't workI recently changed it to what it is currently to be more specific which I thought would hopefully make it work.
so the route is /news/category or /news
PlagueOP
the parent route is /news, category is the sub-path, but, it's where the page.tsx lives.
so it should be /news/category
PlagueOP
Indeed. Doesn't work.
/news/category/ wouldn't work
PlagueOP
I will take off the / and test real quick.
you just tried
revalidatePath("/news/category")
?ok
PlagueOP
didn't work
what cms are you using?
nm contentful
PlagueOP
yeah
I would create a get endpoint and just run revalidatePath there to test
and the path of your page is app/news/category/page.tsx right?
PlagueOP
I'm testing one more thing, I'm sorry, the full route path in my filesystem is /news/category (there is a layout.tsx and loading.tsx in here) then [category]/[page]/page.tsx so now I'm testing
revalidatePath('/news/category/[category]/[page]')
if this doesn't work then I'll make it a GET
ok
PlagueOP
oh got a warning
Warning: a dynamic page path "/news/category/[category]/[page]" was passed to "revalidatePath" without the "page" argument. This has no affect by default, see more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath
guess I need add the type for this
revalidatePath('/news/category/[category]/[page]', 'page')
PlagueOP
wow, so this is required for dynamic paths but it does not mentioned that it's required in the documentation, the example shows it in there, but, I feel like it should defintiely says required.
so does it revalidate?
PlagueOP
Nope
still nothing with this change.
create the get endpoint and test locally
PlagueOP
You can't test locally since it's always dynamic
PlagueOP
the webhook can't hit any endpoint that resolves to localhost
I could test with a button I guess
that's why create the get endpoint
so you can access it easily
I just tested and it work for me
revalidatePath("/news/category/[category]/[page]", "page");
revalidatePath("/news/category/[category]/[page]", "page");
PlagueOP
yeah the page builds fine, what data did you revalidate?
I add or delete the record in database
PlagueOP
didn't work for me even as a GET
you add a post and the page not building?
PlagueOP
the page builds fine. The data doesn't revalidate.
so a new page was builded but some data in the page still using old data?
PlagueOP
I'm not trying to build a new page, I'm trying to revalidate the current ones.
I'm editing content that already exists and when those changes are made the data should be revalidated to reflect those changes, the revalidation is not happening.
I'm editing content that already exists and when those changes are made the data should be revalidated to reflect those changes, the revalidation is not happening.
Thats the issue currently.
What should happen is when the revalidatePath is called the route that I specified that page.tsx should be re-built re-fetching the data in that page and serving that to the user on the next request. None of that is happening and I don't understand why.
well it works for me
PlagueOP
Let me see your code implementation
I add/delete/change data in database
visit the page ensure the page is still old data
visit the page ensure the page is still old data
then i access the get endpoint to revalidate
visit the page again and the data got updated
PlagueOP
yeah that sounds great. No idea why that's not the result I'm seeing.
Can I see the code for your implementation so I can compare to see how the behavior might be different?
PlagueOP
Okay, all of that I'm also doing, how are you accessing the GET endpoint?
just go to browser
PlagueOP
Okay, that also worked for me. Hmm, but it didn't work using a fetch hitting that endpoint, which is the real-world scenerio.
what do you mean a fetch hitting that endpoint?
how did you go to the get endpoint
PlagueOP
the webhook hits the endpoint
via a fetch
me going manually through the browser worked, but, that's not how it's going to be done in the real-world.
you created a new webhook calling the get endpoint?
or you saying the post one
PlagueOP
wait, now it's working via the fetch as well. wtf. Hold on I need to find out what exactly was casuing the problem.
PlagueOP
Okay, I found the weird behavior. Either reading search params, or using if statements (my guess is the search params) within the route handler causes the revalidation to get messed up.
In the example below, the commented out code does not work the
The none commented out code, works as expected. I have no idea wtf is going on anymore.
In the example below, the commented out code does not work the
revalidatePath
function runs, and the correct response is returned but it doesn't actually revalidate.The none commented out code, works as expected. I have no idea wtf is going on anymore.
PlagueOP
Okay the reason the commented out code wasn't working was cause of a typo it works now, so then I guess the reason my original production code wasn't working was because it was a POST instead of a GET request.
The docs need to be updated to say that 1. a GET request is required for revalidating data and 2. the
The docs need to be updated to say that 1. a GET request is required for revalidating data and 2. the
type
parameter for the revalidatePath
function needs to be said that it is REQUIRED when using dynamic routes.Thanks @Ray for helping me debug this, just ended up being user error combined with unspecific documentation.
the searchParams in route handler is in the second arg
PlagueOP
those are route params
PlagueOP
Okay, would love to know.
yes it works
PlagueOP
this right here should work then
try post it yourself instead of webhook
to see if you can get revalidated:true
use
req.headers.get()
instead of headers()
PlagueOP
what would that change or are you just saying as a recommendation?
PlagueOP
True
ty
Yeah it works locally doing it myself in build.
So the webhook is the issue, this sucks.
PlagueOP
I don't know what to think anymore then, the webhook hitting the endpoint should work the same as what we're doing here, the only thing I can think of which wouldn't make any sense is because it's not an actual user triggering the revalidation it never actually builds a new page in the background.
are you sure the webhook will post the data you checking inside the endpoint?
PlagueOP
yeah, I get the correct response.
from webhook?
PlagueOP
yeah
you can check the response on contentful?
PlagueOP
this example uses revalidateTag, which I can't use
yeah I can
I think revalidatePath or revalidateTag work the same
PlagueOP
I would think so as well, but, never know
yeah the only difference between is the different revalidate functions
PlagueOP
Yeah idk this is even more confusing now, looking at the example vercel has I see no reason why this shouldn't be working for me in production with revalidatePath
you hit the post endpoint yourself and it revalidate right?
PlagueOP
yeah
so it works lol
PlagueOP
I need it to work when the webhook hits the endpoint
That's what isn't working and what makes no sense.
should be something wrong on the webhook
what if you remove all the check
PlagueOP
Yeah I simplified the Route Handler to only accept a secret, and that goes through I get the correct response from the webhook which means the revalidatePath function runs, just does do anything in production for some reason
^ what I get back from my webhook
does it revalidate?
PlagueOP
Nope
PlagueOP
Yeah idk, also seems like no one used revalidatePath in route handlers lol everyones using revalidateTag
well it doesn't matter
or you can try with revalidateTag and unstable_cache
PlagueOP
I'm not using unstable APIs in production, but, yeah once that is stable that's exactly what I am going to use.
just trying
but i think you will get same result
PlagueOP
Yeah, hope the caching behaviors improve quickly, because it is by far the best and worst thing about Next.js
For now I'll just make peace with this feature not being implemented the way I want it to.
or use page router for that page
😆
PlagueOP
Appreciate the help though Ray, even if it left me even more confused than before
no problem
PlagueOP
Never got this to work with Route Handlers, moved the api routes to pages and worked instantly as expected. App router caching is still a WIP clearly.
Answer