Next.js Discord

Discord Forum

revalidatePath not working as expected

Unanswered
Devon Rex posted this in #help-forum
Open in Discord
Devon RexOP
I am messing around with revalidatePath and I can't seem to get it working as expected, according to the documentation I should be able to revalidate all data by calling
revalidatePath('/', 'layout')


I have a sidebar that shows the number of current orders and I am seeing 2 different results on 2 different pages. In my layout.tsx I am rendering my sidebar, and I am seeing an outdated number on /admin/tickets and /admin/orders. /admin/orders is showing me the correct updated value but /admin/tickets is showing the old value (The value should increase by 1 when I place a new order) When I place a new order I am calling the above code to revalidate all data. So I should no longer see an outdated value. It doesn't matter how many times I refresh or wait, I still see the old value on the /admin/tickets page which is strange because the sidebar is rendered in the layout so I am not sure why I am seeing different values when I apparently revalidated all data.

57 Replies

Plott Hound
can you share the code where you fetch the data for the sidebar?
@Plott Hound can you share the code where you fetch the data for the sidebar?
Devon RexOP
Just realized it works fine when I run production build locally but doesn't work when hosted on vercel. How I fetch the data for the side bar is like this

export async function getPendingTicketLength() {
    try {
        const tickets = await prisma.ticket.findMany({
            where: {
                status: "pending",
            },
        });

        const pendingTicketsLength = tickets.length;

        return { pendingTicketsLength };
    } catch (error) {
        return { error };
    }
}
Plott Hound
well it looks like you've done everything correctly. this is really strange. are you holding the sidebar data in a state at any point?
Devon RexOP
The sidebar is fully server sided component
Plott Hound
weird. can you show me you layout.tsx please
Devon RexOP
import type { Metadata } from "next";
import "../globals.css";
import Sidebar from "@/components/Admin/Sidebar";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import Provider from "@/components/Provider";

export const metadata: Metadata = {
    title: "Create Next App",
    description: "Generated by create next app",
};

export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {
    return (
        <html lang="en" className="bg-gray-50">
            <body>
                <Provider>
                    <Sidebar />
                    <div className="md:ml-64 md:mt-12">{children}</div>
                    <ToastContainer />
                </Provider>
            </body>
        </html>
    );
}
Plott Hound
am i right in thinking that admin/tickets and /admin/orders share the same layout?
Devon RexOP
Yes that is correct
And I also realized the only reason /admin/orders has the updated value is because I have export const revalidate = 5 in that file
so it seems like revalidatePath just isn't working at all when hosted on vercel because it works fine in my local build
Plott Hound
thanks. is it only the sidebar that is showing stale data?
all i can think is that you aren't calling revalidatePath correctly or its not being called at all. can you show me the server action where you call revalidatePath
then we can log it and pinpoint the issue
Devon RexOP
In my local build I console logged before and after I called revalidatepath and saw both logs, pushed it to vercel and it just doesn't work the same. Here is a whole reddit thread of people saying the same thing. https://www.reddit.com/r/nextjs/comments/12ih2gc/revalidate_not_working_on_deployed_website_using/
console.error("revalidating path");
revalidatePath("/", "layout");
console.error("path revalidated");


And I see the log in vercel
Plott Hound
thats so strange
what version of next are you using? latest?
Devon RexOP
Yes, 14.1.0
Plott Hound
i noticed the path is /webhooks. is this an external webhook that triggers the revalidation?
Devon RexOP
Yes it’s when an order is paid for, stripe sends a webhook and my backend needs to be revalidated
Plott Hound
the fact you are seeing the revalidation in the logs but its not revalidating is very strange indeed. i feel like we've ruled out any potential issue. its especially strange that it works locally in dev but not in prod for vercel
the only other idea i have is to try it with revlaidateTag instead
so add a tag to your fetch and revalidate that from your webhook. i have a similar app and thats how im doing it
Devon RexOP
Does revalidateTag only work with fetch requests? Cause I’m not using a fetch request I’m pulling it from my database with prisma
Plott Hound
are you caching your prisma request with unstable_cache ?
Devon RexOP
No
Plott Hound
im out of ideas sorry. im doing exactly the same thing as you on multiple projects hosted on vercel and we've tried everything i can think of. leave the post up and someone else might know of a solution. you can bump it once per day too. sorry i couldnt help
@Devon Rex Thanks a lot for your time! I’ll update here if I can get it working
Plott Hound
np one last thing actually
try this instead of your current revalidation:
revalidatePath('/admin/tickets');
revalidatePath('/admin/orders');
yeah i know revalidatePath("/", "layout"); should work but sometimes it doesnt
Devon RexOP
That is actually something I haven’t tried, I’m already in bed but I’ll try that out in the morning. Thanks
Plott Hound
fingers crossed. let me know if it works
Devon RexOP
Sadly not, I just resorted to making my pages dynamic since they're frequently updated. Probably shouldve just started with that instead of trying to use revalidatepath
Plott Hound
It’s really odd. I have the exact same setup working here. Might be worth making a minimal repo and reporting this as a bug to GitHub
@Devon Rex Is it working on vercel?
Plott Hound
yes
and on my vps
Devon RexOP
Weird. I was reading online that some packages may interfere with it, not sure how accurate that is
Or could be my browser, I have yet to try it on my phone
Plott Hound
yeah. i think in this situation since you are doing everything correctly the next step would be to make a minimal reproduction of the issue in a new repo and share it. then i can actually see all your code and test it locally. i know that isn't always possible
@Devon Rex Or could be my browser, I have yet to try it on my phone
Plott Hound
shouldn't make a difference, i've tested it on everything
could you show me the whole /api/webhooks file please
Devon RexOP
import Stripe from "stripe";
import { NextRequest, NextResponse } from "next/server";
import { createOrder } from "@/lib/prisma/orders";
import { orderSuccess } from "@/lib/raja/rajaHandler";
import { getServiceById } from "@/lib/prisma/services";
import { revalidatePath } from "next/cache";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
    // https://github.com/stripe/stripe-node#configuration
    apiVersion: "2023-10-16",
});

const webhookSecret: string = process.env.STRIPE_WEBHOOK_SECRET!;

const webhookHandler = async (req: NextRequest) => {
    try {
        const buf = await req.text();
        const sig = req.headers.get("stripe-signature")!;

        let event: Stripe.Event;

        try {
            event = stripe.webhooks.constructEvent(buf, sig, webhookSecret);
        } catch (err) {
            const errorMessage = err instanceof Error ? err.message : "Unknown error";
            // On error, log and return the error message.
            if (err! instanceof Error) console.error(err);
            console.error(`❌ Error message: ${errorMessage}`);

            return NextResponse.json(
                {
                    error: {
                        message: `Webhook Error: ${errorMessage}`,
                    },
                },
                { status: 400 },
            );
        }
        switch (event.type) {
            case "payment_intent.succeeded":
                const serviceName = event.data.object.metadata.serviceName;
                const email = event.data.object.metadata.email;
                const serviceID = event.data.object.metadata.serviceid;
                const quantity = event.data.object.metadata.quantity;
                const discountid = event.data.object.metadata.discount;

                const { service } = await getServiceById(parseInt(serviceID));

                const serviceLabel = service?.getStartedLabel;

                const username = event.data.object.metadata[serviceLabel || ""];

                const amount = event.data.object.amount / 100;

                const { order } = await createOrder({
                    userid: event.data.object.metadata.userid,
                    paymentIntentID: event.data.object.id,
                    email,
                    paymentMethod: "stripe",
                    serviceName,
                    serviceID: parseInt(serviceID),
                    quantity: parseInt(quantity),
                    price: amount,
                    username,
                    discountId: discountid ? parseInt(discountid) : undefined,
                    purchasedAt: new Date(),
                });
                if (order) {
                    const orderNumber = order.id;
                    await stripe.paymentIntents.update(event.data.object.id, {
                        description: `Payment for order number: #${orderNumber}`,
                        metadata: {
                            orderNumber: orderNumber as number,
                        },
                    });
                    await orderSuccess(
                        amount,
                        parseInt(quantity),
                        service,
                        username,
                        parseInt(event.data.object.metadata.userid),
                        email,
                        orderNumber,
                        discountid ? parseInt(discountid) : undefined,
                    );
                    revalidatePath("/", "layout");
                }
                break;
            default:
                break;
        }

        // Return a response to acknowledge receipt of the event.
        return NextResponse.json({ received: true });
    } catch {
        return NextResponse.json(
            {
                error: {
                    message: `Method Not Allowed`,
                },
            },
            { status: 405 },
        ).headers.set("Allow", "POST");
    }
};

export { webhookHandler as POST };
Plott Hound
thanks
this looks fine to me so im really at a loss as to why revalidate isnt working
@Ray sorry to ping you but if you get 5mins free could you take a look at this and see if i missed anything? revalidatePath is working in local prod but not vercel prod. thanks
@Devon Rex console.error("revalidating path"); revalidatePath("/", "layout"); console.error("path revalidated"); And I see the log in vercel
Plott Hound
they confirmed that revalidate path is being run but its not doing anything and there is no other caching afaik
Devon RexOP
Is it possible for revalidatePath to fail for any reason?
Plott Hound
sometimes if the path is wrong
here is the most simplified example i could make where it works for me:
import { NextRequest, NextResponse } from 'next/server'
import { revalidateTag } from 'next/cache'

export async function POST(request: NextRequest) {
  const requestHeaders = new Headers(request.headers)
  const secret = requestHeaders.get('x-vercel-reval-key')

  if (secret !== process.env.CONTENTFUL_REVALIDATE_SECRET) {
    return NextResponse.json({ message: 'Invalid secret' }, { status: 401 })
  }

  revalidateTag('posts')

  return NextResponse.json({ revalidated: true, now: Date.now() })
}
im using revalidateTag but it works the same if i use Path, i just target the fetch tag directly instead of a single path
@Plott Hound try this instead of your current revalidation: revalidatePath('/admin/tickets'); revalidatePath('/admin/orders');
yea try this, revalidatePath("/", "layout") is odd something lol