Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revalidatePath with middleware doesn't work on multi-tenant project #59825

Open
1 task done
sergiolamorda opened this issue Dec 20, 2023 · 10 comments
Open
1 task done
Labels
bug Issue was opened via the bug report template. Pages Router Related to Pages Router. Runtime Related to Node.js or Edge Runtime with Next.js.

Comments

@sergiolamorda
Copy link

Link to the code that reproduces this issue

https://github.com/sergiolamorda/nextjs-issue-revalidatePath-multitenant

To Reproduce

First, add two domains to your local hostname, for example:

127.0.0.1	test1.local
127.0.0.1	test2.local

hen, execute the build and start commands:

npm run build
npm run start

Next, open two different tabs and navigate to http://test1.local:3000/test and http://test2.local:3000/test respectively.

Now, make a POST request to http://localhost:3000/api/revalidate, which will execute revalidatePath('/test2.local/test'). If you reload the previous tab, the content does not update.

Finally, make a POST request to http://localhost:3000/api/revalidate2, which will execute revalidatePath('/test'). If you reload the previous tabs, both will show updated content.

Current vs. Expected behavior

We're working on a multi-tenant project that employs middleware to rewrite requests, converting the host into a slug.

In Next.js 12, you can perform on-demand revalidation in the API using res.revalidate(), which only invalidates the cache for the domain where the request was made.

However, after migrating to Next.js 14, we encountered issues when trying to invalidate the cache with revalidatePath; it doesn't seem to work as expected.

Attempting revalidatePath('/domain/test') doesn't take the middleware into account and fails to produce any effect.

On the other hand, using revalidatePath('/test') results in invalidating the /test path across all domains.

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11 Pro
Binaries:
  Node: 20.9.0
  npm: N/A
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 14.0.4
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0
  typescript: N/A
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Data fetching (gS(S)P, getInitialProps), Middleware / Edge (API routes, runtime)

Additional context

No response

@sergiolamorda sergiolamorda added the bug Issue was opened via the bug report template. label Dec 20, 2023
@github-actions github-actions bot added Pages Router Related to Pages Router. Runtime Related to Node.js or Edge Runtime with Next.js. labels Dec 20, 2023
@rogerbandera
Copy link

We have the same issue, we cannot update version. Hopefully it will be solved as soon as possible...

@sergiolamorda
Copy link
Author

Here's an example of what's happening to me.
My route structure is as follows:
[domain]/[[…slug]]

I execute revalidatePath('/test2.local')

Browser Result
http://test1.local/test1.local Keeps cache
http://test1.local/test2.local Invalidates cache
http://test2.local/test1.local Keeps cache
http://test2.local/test2.local Invalidates cache

If I execute revalidatePath('/test2.local/test2.local')

Browser Result
http://test1.local/test1.local Keeps cache
http://test1.local/test2.local Keeps cache
http://test2.local/test1.local Keeps cache
http://test2.local/test2.local Keeps cache

It should be noted that the middleware rewrites the parameters so that the browser correctly interprets the route structure of Next.js.
url.pathname = '/${hostname}${pathname}'

We are currently using version 14.0.4
I don't understand what could be happening, it seems like there might be a bug. What do you think?

@eric-burel
Copy link
Contributor

eric-burel commented Jan 8, 2024

I confirm I can reproduce OP issue and it's super unsettling, both the fact that the first revalidate on "/localhost/test" doesn't work while having a correct path, and the revalidate2 does work on "/test" despite having a wrong path.

I've also tried with [slug] instead of [...slug], and tweaked the code to revalidate directly on localhost.

This feels like as some point the URL users sees is used a cache key, instead of the underlying path in the Next.js app. But surprising because this seems to affect the server cache, since the value is also not updated when opening the page with another browser.

@sergiolamorda
Copy link
Author

I have delved a bit more into this error and I am sharing it in case it helps.

I have found that the tags generated for the pages are created from the request urlPathname, and this value does not take into account the rewrite of the middleware.

urlPathname: request.nextUrl.pathname,

const parsedPathname = new URL(urlPathname, 'http://n').pathname

This causes the tags to be associated with the user's request and not with the actual structure of the project.

@LouisCuvelier
Copy link

LouisCuvelier commented Feb 8, 2024

I was facing this kind of issue also. I found a workaround explained here : amannn/next-intl#846
Maybe it could work for you ?

@B33fb0n3
Copy link

B33fb0n3 commented Feb 19, 2024

The "solution" that I am currently use is by using unstable_cache. With that, you are able to control the data and also the pages, even when the pages are "forced-static". You can take a look at this repository, that shows how I resolved it: https://github.com/B33fb0n3/revalidaterewrites

To test it,
Go to /helpi
and
/helpi?host=someotherhost.com

Click the "revalidate" Button on the page "/helpi" (without query params). You see, that "somehost.com" gets revalidated and "someotherhost.com" not. Even when they are rewritten.

I found this in the vercel platform example: https://github.com/vercel/platforms/

@reinvanhaaren
Copy link

I have the same issue. I'm rewriting to different subfolders by mapping the request header host to /${host}. The UI is not showing fresh data after revalidating.

When I call a server function from a server component containing revalidateTag or revalidatePath, the cache is invalidated (I see the new data in the .next/cache/fetch-cache/ folder), the unstable_cache function is executed on revalidation (I see my console.log on the server), but the UI won't reflect the updated data in the same roundtrip when I'm on a route rewritten by middleware. I have to perform a hard refresh of my browser window to see the updated data on my page.

@reinvanhaaren
Copy link

After a few hours of debugging I noticed i alter the headers in my NextResponse.rewrite. When I comment this line out, the data is reflected in the way you expect it to work on revalidation.

Turns out I added the user's request headers to the response 🤦🏼‍♂️

   // Add a header to the response to indicate path in layout
-  const responseHeaders = new Headers(request.headers);
+  const responseHeaders = new Headers();
   responseHeaders.set("x-frontend-path", path);
 
   const response = NextResponse.rewrite(
     new URL(`/${path}${fullPath === "/" ? "" : fullPath}`, request.url),
     { headers: responseHeaders },
   );

@DzTheRage
Copy link

I ended up just going with revalidateTag() instead of revalidatePath() to get around this issue.

@Zonalds
Copy link

Zonalds commented Apr 23, 2024

Using revalidateTag works fine. You can assign dynamic tag to force revalidate for individual tenant.

Something like this:

async function getSettings(domain) {
    const token = process.env.API_TOKEN
    const fetchUrl = `${process.env.API_URL}/api/settings?domain=${domain}`
    const settingsData = await fetch(`${fetchUrl}`, {
        headers: {
            Authorization: `Bearer ${token}`,
        },
        cache: "force-cache",
        next: { tags: [`site-settings-${domain}`] }
    }).then((res) => res.json())
    console.log({ settingsData })
    return settingsData?.data
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template. Pages Router Related to Pages Router. Runtime Related to Node.js or Edge Runtime with Next.js.
Projects
None yet
Development

No branches or pull requests

8 participants