vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.4k stars 26.9k forks source link

Unclear caching behavior in nextjs 14 and 15 #71881

Open mastoj opened 5 hours ago

mastoj commented 5 hours ago

Link to the code that reproduces this issue

https://github.com/mastoj/wrapped-fetch-debug

To Reproduce

I have the exact same app in nextjs 14: https://github.com/mastoj/wrapped-fetch-bug-14 and nextjs 15: https://github.com/mastoj/wrapped-fetch-debug

What we are doing is testing what is happening when you wrap fetch in a higher order function, since that is a natural way to componse functions together.

Both of the solution is not behaving as I expect them to. To reproduce the issue in any of the repos do:

  1. pnpm run build
  2. pnpm run start
  3. http://localhost:3000/debug
  4. http://localhost:3000/debug

Current vs. Expected behavior

Doing the first load will print the logs from the api/route:

==> Getting some data: /api/data/1 ==> Getting some data: /api/data/2 ==> Getting some data: /api/data/3

which is as expected. Doing the second load of the page is what makes things interesting. In our /debug page we are making three api-calls in a slightly different way, but I expect them all to be cached.

Nextjs 14 behavior

For some reason the fetch call that is wrapped using a higher order function won't cache at all and we get the following output:

==> Getting some data: /api/data/2

Nextjs 15 behavior

In 15 the behavior differed quite a lot from 14. To some degree it make sense since the defaults has changed. Since we are using explicit const dynamic and revalidate on fetch I would actually expect the behavior to be the same, but that is not the case. In 15 it seems like nothing is cached and we make api calls every time.

==> Getting some data: /api/data/1 ==> Getting some data: /api/data/2 ==> Getting some data: /api/data/3

Expected behavior

Even though we have export const dynamic = "force-dynamic" I would expect the revalidate on the fetch calls to be honored. That assumptions means I would expect no outputs from the log from the routes to be outputted since no calls should be made.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.6.0: Mon Jul 29 21:13:04 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6020
  Available memory (MB): 65536
  Available CPU cores: 12
Binaries:
  Node: 20.14.0
  npm: 10.7.0
  Yarn: 1.22.22
  pnpm: 9.9.0
Relevant Packages:
  next: 14.2.8 // An outdated version detected (latest is 15.0.1), upgrade is highly recommended!
  eslint-config-next: 14.2.8
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 5.6.3
Next.js Config:
  output: N/A
 ⚠ An outdated version detected (latest is 15.0.1), upgrade is highly recommended!
   Please try the latest canary version (`npm install next@canary`) to confirm the issue still exists before creating a new issue.
   Read more - https://nextjs.org/docs/messages/opening-an-issue

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

Not sure

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

next start (local), Vercel (Deployed)

Additional context

I have verified the behavior both locally and on vercel.

gruckion commented 5 hours ago

Typo. “ For some reason the fetch call that is wrapped using a higher order function want cache at all and we get the following output:”.

won’t cache**

gruckion commented 5 hours ago

Force dynamic is used to ensure that the request is generated at request time. It is not used for caching.

https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic

As for using revalidate in the fetch request. You are not using the ‘cache’ attribute as well. Which in NextJS 15 defaults to no store.

https://nextjs.org/docs/app/api-reference/functions/fetch

So you will need to set that if you want the fetch request to be cached.

Note the docs say:

As a convenience, it is not necessary to set the cache option if revalidate is set to a number.

But I believe this is only true for NextJS 14. Please try setting ‘“cache”: “force-cache”,’ and try again if you are using NextJS 15.

You can read more about this change here.

https://nextjs.org/blog/next-15 https://nextjs.org/blog/next-15-rc

image

mastoj commented 4 hours ago

(fixed typo)

Added cache: "force-cache" with no difference in behavior: https://github.com/mastoj/wrapped-fetch-debug/commit/dff943b2babfda5313874217f9c8cdbc9964cc2b