Next.js Discord

Discord Forum

revalidatePath in Route Handlers

Answered
Plague posted this in #help-forum
Open in Discord
Avatar
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 unstable_cache becomes stable, I cannot tag my data since I fetching using the CMS client and not the fetch API.
Image
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.
View full answer

152 Replies

Avatar
I'd appreciate any clarification/help with this as this has been driving me insane the past few weeks.
Avatar
(/path/1234)
(/path/[slug]/page)
(/path/[slug], 'page')
all work
have you tried to call the endpoint manually and does it revalidate?
Avatar
I have not tried calling it manually, not sure how that would differ from the webhook calling it?
Avatar
no differ but to see if it work or not?
and have you checked does it actually hit the endpoint?
Avatar
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.
Avatar
can you show the code?
Avatar
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:
Image
Avatar
have you tried revalidatePath("/news/category")
Avatar
No, in the documentation they use single quotes, but, there shouldn't be a difference either way.
Avatar
not the quote
but the slash after the path
Avatar
oh
Avatar
/news/category vs /news/category/
Avatar
I see
No, but, I did use /news before and it also didn't work
I recently changed it to what it is currently to be more specific which I thought would hopefully make it work.
Avatar
so the route is /news/category or /news
Avatar
the parent route is /news, category is the sub-path, but, it's where the page.tsx lives.
Avatar
so it should be /news/category
Avatar
Indeed. Doesn't work.
Avatar
/news/category/ wouldn't work
Avatar
I will take off the / and test real quick.
Avatar
you just tried revalidatePath("/news/category")?
ok
Avatar
didn't work
Avatar
what cms are you using?
nm contentful
Avatar
yeah
Avatar
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?
Avatar
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
Avatar
ok
Avatar
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
Avatar
revalidatePath('/news/category/[category]/[page]', 'page')
Avatar
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.
Avatar
so does it revalidate?
Avatar
Nope
still nothing with this change.
Avatar
create the get endpoint and test locally
Avatar
You can't test locally since it's always dynamic
Avatar
why?
next build then next start
not next dev
Avatar
the webhook can't hit any endpoint that resolves to localhost
I could test with a button I guess
Avatar
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");
Image
Image
Avatar
yeah the page builds fine, what data did you revalidate?
Avatar
I add or delete the record in database
Avatar
didn't work for me even as a GET
Avatar
you add a post and the page not building?
Avatar
the page builds fine. The data doesn't revalidate.
Avatar
so a new page was builded but some data in the page still using old data?
Avatar
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.
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.
Avatar
well it works for me
Avatar
Let me see your code implementation
Avatar
I add/delete/change data in database
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
Avatar
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?
Avatar
just a simple test
Image
Image
Avatar
Okay, all of that I'm also doing, how are you accessing the GET endpoint?
Avatar
just go to browser
Avatar
Okay, that also worked for me. Hmm, but it didn't work using a fetch hitting that endpoint, which is the real-world scenerio.
Avatar
what do you mean a fetch hitting that endpoint?
how did you go to the get endpoint
Avatar
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.
Avatar
you created a new webhook calling the get endpoint?
or you saying the post one
Avatar
wait, now it's working via the fetch as well. wtf. Hold on I need to find out what exactly was casuing the problem.
Avatar
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 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.
Image
Avatar
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 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.
Avatar
the searchParams in route handler is in the second arg
Avatar
those are route params
Avatar
ah yeah i forgot
well but post request should work too
let me test real quick
Avatar
Okay, would love to know.
Avatar
yes it works
Avatar
Image
this right here should work then
Avatar
try post it yourself instead of webhook
to see if you can get revalidated:true
use req.headers.get() instead of headers()
Avatar
what would that change or are you just saying as a recommendation?
Avatar
headers is for server component
Image
Avatar
True
ty
Yeah it works locally doing it myself in build.
So the webhook is the issue, this sucks.
Avatar
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?
Avatar
yeah, I get the correct response.
Avatar
from webhook?
Avatar
yeah
Avatar
you can check the response on contentful?
Avatar
this example uses revalidateTag, which I can't use
yeah I can
Avatar
I think revalidatePath or revalidateTag work the same
Avatar
I would think so as well, but, never know
yeah the only difference between is the different revalidate functions
Avatar
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
Avatar
you hit the post endpoint yourself and it revalidate right?
Avatar
yeah
Avatar
so it works lol
Avatar
I need it to work when the webhook hits the endpoint
That's what isn't working and what makes no sense.
Avatar
should be something wrong on the webhook
what if you remove all the check
Avatar
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
Image
^ what I get back from my webhook
Avatar
does it revalidate?
Avatar
Nope
not sure if its related
Avatar
Yeah idk, also seems like no one used revalidatePath in route handlers lol everyones using revalidateTag
Avatar
well it doesn't matter
or you can try with revalidateTag and unstable_cache
Avatar
I'm not using unstable APIs in production, but, yeah once that is stable that's exactly what I am going to use.
Avatar
just trying
but i think you will get same result
Avatar
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.
Avatar
or use page router for that page
😆
Avatar
:kekw:
Appreciate the help though Ray, even if it left me even more confused than before :kekw:
Avatar
no problem
Avatar
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