Next.js Discord

Discord Forum

How exactly does `fetch()` work in Next.js?

Unanswered
Atlantic cod posted this in #help-forum
Open in Discord
Atlantic codOP
According to the docs here https://nextjs.org/docs/app/building-your-application/caching#request-memoization ("Good to know" section), it is mentioned that:

Request memoization is a React feature, not a Next.js feature

But also, on this page https://nextjs.org/docs/app/api-reference/functions/fetch, the first example there includes the following:

export default async function Page() {
  // ...
 
  // This request should be cached with a lifetime of 10 seconds.
  // Similar to `getStaticProps` with the `revalidate` option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })
 
// ...
}


This suggests that this fetch used in the Page component (a server component) uses a Next.js feature. It uses FetchOptions.next.revalidate which isn't from React.

From my understanding, there are two behaviours of fetch (not sure if that makes sense), one does memoization, which only caches for a single render pass, and (according to the caching docs linked above):

It applies to fetch requests in generateMetadata, generateStaticParams, Layouts, Pages, and other Server Components

The other one is for use in route handlers and server actions, and it gives access to the Nextjs data cache, and the cache is kept across server requests.

So here are my questions:

- What exactly is the behaviour of fetch inside server components, does it do memoization or caches with Next.js Data Cache? If memoization, then why does the example include a { next: { revalidate: 10 } } option?

- In server actions and route handlers, fetch() mainly works with the Next.js Data cache. Is that right?

- What is the behaviour of fetch() in a middleware? According to this page: https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#fetch-api
You can use fetch in Server Components, Route Handlers, and Server Actions
So, what of middleware? fetch() works there too...

19 Replies

@Atlantic cod According to the docs here https://nextjs.org/docs/app/building-your-application/caching#request-memoization ("Good to know" section), it is mentioned that: > Request memoization is a React feature, not a Next.js feature But also, on this page https://nextjs.org/docs/app/api-reference/functions/fetch, the first example there includes the following: ts export default async function Page() { // ... // This request should be cached with a lifetime of 10 seconds. // Similar to `getStaticProps` with the `revalidate` option. const revalidatedData = await fetch(`https://...`, { next: { revalidate: 10 }, }) // ... } This suggests that this `fetch` used in the `Page` component (a server component) uses a Next.js feature. It uses `FetchOptions.next.revalidate` which isn't from React. From my understanding, there are two behaviours of `fetch` (not sure if that makes sense), one does memoization, which only caches for a single render pass, and (according to the caching docs linked above): > It applies to fetch requests in `generateMetadata`, `generateStaticParams`, Layouts, Pages, and other Server Components The other one is for use in route handlers and server actions, and it gives access to the Nextjs data cache, and the cache is kept across server requests. So here are my questions: - What exactly is the behaviour of `fetch` inside server components, does it do memoization or caches with Next.js Data Cache? If memoization, then why does the example include a `{ next: { revalidate: 10 } }` option? - In server actions and route handlers, `fetch()` mainly works with the Next.js Data cache. Is that right? - What is the behaviour of `fetch()` in a middleware? According to this page: https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#fetch-api > You can use fetch in Server Components, Route Handlers, and Server Actions So, what of middleware? `fetch()` works there too...
heya welcome, i'll answer some of your questions according to what i know:

What exactly is the behaviour of fetch inside server components, does it do memoization or caches with Next.js Data Cache? If memoization, then why does the example include a { next: { revalidate: 10 } } option?

it's important to know that request memoization is different from caching with Next's data cache.

let's say we have a parent component that does a fetch:
export async function ParentPage() {
  const data = fetch("example.com/api/getData", { ... });

  return (
    <>
      <ChildComponent />
      {/* do something with data */}
    </>
  );
}


And its child component happens to do the same fetch as well:
export async function ChildComponent() {
  const data = fetch("example.com/api/getData", { ... });
  // ..
}


request memoization is a feature that "squashes" these two fetch calls into just one.

imagine having a long hirearchy of components that needs to fetch the same data. We might think of using a global object to fetch and store that data to prevent duplicate fetches, then share it among components; but with next, you can just call the same fetch calls without worrying that it will do the request twice.

next's data cache comes after the request memoization. after the requests are "squashed", the cache will then be consulted according to each of the requests. The cache will either be "miss", marked as "stale" or be "hit".

the doc explains this really well, you should give it a read once more, or ask AI on #gpt-help since it's well trained on those docs.
In server actions and route handlers, fetch() mainly works with the Next.js Data cache. Is that right?

afaik the fetch API behaves the same way in server actions and route handlers, as to when it is used in RSCs. Though you might want to try it out first to verify.

What is the behaviour of fetch() in a middleware?

fun fact, the only way to do IO in middleware is by making use of fetch due to its edge runtime limitation. you are able to use fetch there as well, as for the data cache and the sorts, I believe it does apply as well. again, you might want to try this out first to verify though
@iyxan23 heya welcome, i'll answer some of your questions according to what i know: > What exactly is the behaviour of fetch inside server components, does it do memoization or caches with Next.js Data Cache? If memoization, then why does the example include a { next: { revalidate: 10 } } option? it's important to know that request memoization is different from caching with Next's data cache. let's say we have a parent component that does a fetch: tsx export async function ParentPage() { const data = fetch("example.com/api/getData", { ... }); return ( <> <ChildComponent /> {/* do something with data */} </> ); } And its child component happens to do the same fetch as well: tsx export async function ChildComponent() { const data = fetch("example.com/api/getData", { ... }); // .. } request memoization is a feature that "squashes" these two fetch calls into just one. imagine having a long hirearchy of components that needs to fetch the same data. We might think of using a global object to fetch and store that data to prevent duplicate fetches, then share it among components; but with next, you can just call the same fetch calls without worrying that it will do the request twice. next's data cache comes after the request memoization. after the requests are "squashed", the cache will then be consulted according to each of the requests. The cache will either be "miss", marked as "stale" or be "hit". the doc explains this really well, you should give it a read once more, or ask AI on <#1089389297548931182> since it's well trained on those docs.
Great explanation of the fetch mechanics in next. If your hosting with vercel be very careful with fetching in your middleware, there are limitations to response time.
@iyxan23 heya welcome, i'll answer some of your questions according to what i know: > What exactly is the behaviour of fetch inside server components, does it do memoization or caches with Next.js Data Cache? If memoization, then why does the example include a { next: { revalidate: 10 } } option? it's important to know that request memoization is different from caching with Next's data cache. let's say we have a parent component that does a fetch: tsx export async function ParentPage() { const data = fetch("example.com/api/getData", { ... }); return ( <> <ChildComponent /> {/* do something with data */} </> ); } And its child component happens to do the same fetch as well: tsx export async function ChildComponent() { const data = fetch("example.com/api/getData", { ... }); // .. } request memoization is a feature that "squashes" these two fetch calls into just one. imagine having a long hirearchy of components that needs to fetch the same data. We might think of using a global object to fetch and store that data to prevent duplicate fetches, then share it among components; but with next, you can just call the same fetch calls without worrying that it will do the request twice. next's data cache comes after the request memoization. after the requests are "squashed", the cache will then be consulted according to each of the requests. The cache will either be "miss", marked as "stale" or be "hit". the doc explains this really well, you should give it a read once more, or ask AI on <#1089389297548931182> since it's well trained on those docs.
Atlantic codOP
Thanks for the response.

I do know that memoization is different from Next.js data cache. I thought React was responsible for caching. However, they said Request memoization is a react feature. I'm curious what this would look like without Nextjs. What exactly is getting memoized? From the Nextjs docs:

If a cached response is found, it's returned immediately and memoized.

which would mean that the response is what's getting memoized. If that's the case, what then is the purpose of memoization? Nextjs can just see if it's in the cache and return it, or make the request otherwise.
If the response is not what is getting memoized, then is it the request params? If that's the case when React determines if the request is sent or not. In that case, what's the purpose of Next.js cache?

Again, I'd like to see what the memoization looks like without Next.js if possible. Thanks
Take a look at this it’s really comprehensive.
@Jboncz https://stackoverflow.com/questions/78176223/whats-the-difference-between-react-18s-new-cache-and-next-js-deduping
Atlantic codOP
Thanks. I know about the difference between react cache and Nextjs cache. (I also did check the SO link.) What I'm looking for is what exactly react memoizes if all the requests go through Next.js anyway (which can already decide if there's a cached value or not and return a response or make the request as necessary). Where does react memoization come in and why is it needed?
It doesn’t dedupe the requests proper. If you have 5 components that all need the data from an endpoint.

React sees that you’re making the same call all 5 times and deduplicates it. So it’s only calling the fetch once.

Without react cache you would have to fetch at the higher level and pass down the reference to the 5 components.

Unstable cache doesnt doesn’t do that. I’m on mobile so apologies for any typos or weird formatting.
If you were fetching through an orm and not wrapping it in the react cache it would query the database as many times as you called it.

If you wrapped it in react cache itwould hit it once. If you wrap it in the react cache and unstable cache, it would check to see if it needs to be revalidated and hit it once. If you only wrapped it in unstable cache it would call it however many times but it would stop at the server and not hit the database as unstable cache is still valid.
If this is the Next.js fetch, then it uses the data cache like unstable_cache. From the docs, there's the following example for unstable_cache:

import { getUser } from './data';
import { unstable_cache } from 'next/cache';
 
const getCachedUser = unstable_cache(
  async (id) => getUser(id),
  ['my-app-user']
);
 
export default async function Component({ userID }) {
  const user = await getCachedUser(userID);
  ...
}


From this, is there any need for memoization from React? One can just import and use getCachedUser and the request will still only be made once in this case.
If I’m not mistaken react removed their monkey-patch of the fetch function and nextjs redid what they stoped doing to a degree. See
@Jboncz If I’m not mistaken react removed their monkey-patch of the fetch function and nextjs redid what they stoped doing to a degree. See
Atlantic codOP
Yeah, I read that react removed it, but why is it said in the Next.js docs then that request memoization is a react feature?
I’m about to head to bed I will respond more tomorrow. There’s so many different caches that sometimes I get confused at this point.
I’m not sure exactly the answer to your question without speculating.
@Jboncz I’m about to head to bed I will respond more tomorrow. There’s so many different caches that sometimes I get confused at this point.
Atlantic codOP
Lol. I too just wanted to get everything well indexed in my head once and for all
Yeah I understand… it’s confusing to a lot of people myself included. For the most part I let react do its thing and I use unstable cache. But I get why you’re asking the question maybe someone will have better insight.