vercel / next.js

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

Caching data doesn't work in middleware #48169

Open SiarheiLazakovich opened 1 year ago

SiarheiLazakovich commented 1 year ago

Verify canary release

Provide environment information

Operating System:
      Platform: linux
      Arch: x64
      Version: #22 SMP Tue Jan 10 18:39:00 UTC 2023
    Binaries:
      Node: 16.17.0
      npm: 8.15.0
      Yarn: 1.22.19
      pnpm: 7.1.0
    Relevant packages:
      next: 13.3.1-canary.3
      eslint-config-next: N/A
      react: 18.2.0
      react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

https://codesandbox.io/p/sandbox/wandering-worker-7g6q59

To Reproduce

  1. open codesandbox example
  2. open console
  3. see such log image

Describe the Bug

React cache() doesn't work in middleware.

Expected Behavior

In middleware should be used cached data for same request.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

RETOVSKEJ commented 1 year ago

same in 13.2.4

IonelLupu commented 1 year ago

I just found this too, but when I used the fetch function in middleware.

In my case, I don't want to cache the data, but I was surprised to see, in middleware, it doesn't cache the response by default. I didn't have to specify cache: 'no-store'.

Is this a bug or expected behaviour? (thought, might be a different bug so I might need to create another Github issue)

AdrianFahrbach commented 1 year ago

Do you have an update in this? Is there a way to cache data in the middleware?

jthrilly commented 12 months ago

Just to add my voice to others, I too was surprised by this, along with the fact that middleware can't be opted out of the edge runtime which therefore blocks the direct calling of databases in many cases.

I suspect this "edge only" implementation is going to be why this can't be implemented. It follows that if these are serverless functions they can't have any form of persistent state.

I read elsewhere that this is a deliberate design decision to prevent people overloading middleware, but for the cynical among us it looks more like an attempt to force people onto vercel. After all, if deploying to a normal nodejs environment is fully supported (as per the docs) there is really no need to force this limitation.

Please, at least let us opt out of edge middleware so we can use caching and direct DB access.

awinogrodzki commented 11 months ago

Hey, I created an issue and discussion on this a while ago.

I inspected Next.js code and it seems that caching is just not available inside Edge Function.

I understand the reason, but eventually it will force people out of Vercel's infrastructure.

Please upvote the discussion and let's make Middleware on Edge Function optional.

skolodyazhnyy commented 7 months ago

Any news on this? Cache does not work in standalone mode too. Lack of cache and "different fetch behaviour" comes as a surprise.

...although re-reading this issue I'm not sure it describes my issue. The title seems like it's about caching data (ie. fetch cache) but the actual description is "React cache() doesn't work in middleware" which is not the same as caching responses in fetch.

Is this issue about the fact that fetch requests are not cached in the middleware?

Arinji2 commented 6 months ago

Seems like a pretty big issue since the middleware is used a lot by us for checking for legacy links which aren't going to change, so we really need to cache the db responses.

On 14.1.0

Arinji2 commented 6 months ago

Found a workaround, make your data fetching server actions, cache those and call the functions in your middleware

cpotey commented 5 months ago

To add to this, if you wrap your fetch requests in React's cache - like

import { cache } from 'react'

export const getUser = cache(async (): Promise<User | null> => {
  const res = await fetch(`${API_ENDPOINT}/user/?`, {
    headers: {
      Cookie: `cookieId=${cookies().get('cookieId')?.value}`,
    },
  });
  if (!res.ok) {
    return null;
  }
  const data = await res.json();

  return data
});

And then try to use this function in your middleware, it doesn't work, and you get a TypeError: (0 , react__WEBPACK_IMPORTED_MODULE_1__.cache) is not a function error...

We're not able to use a fetch without a cache (as we depend on cookies), so need to use React cache for memoization, but this doesn't work with middleware at all.

Would really be good to look into this, currently we're going to have to duplicate all of our functions so that there's a version of them without the wrapping cache (for use exclusively in the middleware.ts)

Plus, because middleware hits for multiple things on each route - the same request to the API could be fired around 3/4 times per page load (since there's no memoization!)

jnm733 commented 5 months ago

That feature is essential for me

mattddean commented 4 months ago

@Arinji2 would you mind describing how to do this? I tried creating a server action which makes a fetch request with a revalidate setting and calling it from middleware but it doesn't seem to use the fetch cache.

Found a workaround, make your data fetching server actions, cache those and call the functions in your middleware

Arinji2 commented 4 months ago

@Arinji2 would you mind describing how to do this? I tried creating a server action which makes a fetch request with a revalidate setting and calling it from middleware but it doesn't seem to use the fetch cache.

Found a workaround, make your data fetching server actions, cache those and call the functions in your middleware

Are you sure? Do you have the url logging thing enabled? If yes.. show your code cause last time I checked.. it worked.

AhmedBaset commented 4 months ago

The Sandbox linked in the issue doesn't include a middleware file, which makes it not clear what you're trying to accomplish.

React.cache is designed to function solely within the React rendering process and is not effective outside of React Server Components. Middleware is not just not a RSC, It's run in a seperate context and seperate environment.

cache is only for use with React Server Components. See frameworks that support React Server Components.

https://react.dev/reference/react/cache

It appears that some people in the comments might misunderstand the functionality of React.cache. It doesn't cache responses for future use (not Data Cache); you can use unstable_cache for that purpose. Its goal is to memoize function execution during rendering, enabling the function to be called with the same parameters in different parts of the React Tree, with React ensuring it fires only once.

That being said, Middleware isn't the best part to fetch data as it runs before every request:

Recognizing situations where middleware may not be the optimal approach is just as crucial. Here are some scenarios to be mindful of:

  • Complex Data Fetching and Manipulation: Middleware is not designed for direct data fetching or manipulation, this should be done within Route Handlers or server-side utilities instead.
  • ...
  • Direct Database Operations: Performing direct database operations within Middleware is not recommended. Database interactions should done within Route Handlers or server-side utilities.

    https://nextjs.org/docs/app/building-your-application/routing/middleware

vaishnavi-jadhav-amla commented 2 months ago

I am facing the same issue. Do we have any updates here?

Edit by maintainer bot: Comment was automatically minimized because it was considered unhelpful. (If you think this was by mistake, let us know). Please only comment if it adds context to the issue. If you want to express that you have the same problem, use the upvote 👍 on the issue description or subscribe to the issue for updates. Thanks!

mawiramawira commented 2 months ago

This is to deliberately lock us into vercel architecture without a doubt

SartoRiccardo commented 4 weeks ago

Found another workaround for this (Next 14.2.5), make a route in the api folder and use it to proxy the request. For example, to cache a request to https://www.example.com/api/users, do:


/api/mwcache/users/route.js (example path, named it like this out of convenience)

import { NextResponse } from "next/server";

export async function GET(request) {
  const fetchResp = await fetch(
    "https://www.example.com/api/users",
    { next: { revalidate: 3600 } }  // Or any other cache rule you want
  )
  // Minimal example but you can forward also headers and response status and other things as you need them
  return NextResponse.json(await fetchResp.json());
}

middleware.js

export default async function middleware(request) {
  const cachedResp = await fetch(`${process.env.HOST}/api/mwcache/users`);  // Set HOST in .env
  const cachedData = await cachedResp.json();
  // Do things with you cached data...
}

{HOST}/api/mwcache/users is not cached and will get called every time but https://www.example.com/api/users will be cached as per the rules you set.