Next.js Discord

Discord Forum

Cache fetch requests in middleware?

Answered
Silver Fox posted this in #help-forum
Open in Discord
Avatar
Silver FoxOP
Can you cache fetch requests in middleware.ts?

e.g. from an external route

export default async function middleware(request: NextRequest) {
    const redirects = await fetch(`www.external.com/api/redirects`, {
      next: { tags: ['redirect-cache'] },
      cache: 'force-cache',
    });

     ...
});


e.g. from a next.js route

export default async function middleware(request: NextRequest) {
    const redirects = await fetch(`${basePath}/api/redirects`, {
      next: { tags: ['redirect-cache'] },
      cache: 'force-cache',
    });

     ...
});
Answered by joulev
Nextjs cache doesn’t work there. But since you are self hosting, I suppose you can simply cache in the global object.

Like if global.redirectRules doesn’t exist, then fetch the rules, then save the rules into global.redirectRules. If the global variable already exists, use it.

You can also route requests to be redirected to a route handler, where cache works, then redirect from that route handler.

Server actions are irrelevant here. Do not use them where you don’t need them.
View full answer

22 Replies

Avatar
Alfonsus Ardani
your middleware arent supposed to make fetch calls. its supposed to be lightweight. In vercel, it will also run in a different location than your serverless function so its designed to be "self-contained" thats why fetch behavior is the default cache behavior
if you want fetch caching, make a reusable function and use them in your route handlers
Avatar
Silver FoxOP
Ty for the response!

In vercel
We are self-hosting, so it won't be run in a different location

if you want fetch caching, make a reusable function and use them in your route handlers
We are trying to programatically redirect based on a 3rd party list of redirects. So we need to look at every request to decide if we should redirect. Would love to cache this call and handle cache invalidation inside of the middleware, rather than rolling our own
Avatar
Alfonsus Ardani
hmm
thats a solid use case
Avatar
Arinji
@Silver Fox i had this same issue once
the best method would be to make a server action, give its fetch calls all your caching options and call it
route handlers work as well, but i like the feel of server actions
normal functions wont work btw
Avatar
Silver FoxOP
@Arinji interesting. Did this make it more performant than just having it in the middleware? This is what I have at the minute...

// middleware.ts
const cachedRequests = null;

export default async function middleware(request) {
  const fetchRedirects = async () => {
    const res = await (
      await fetch(`..../redirects`)
    ).json()
    return res['redirects']
  }

  if (!cachedRequests) {
    cachedRequests = await fetchRedirects()
  } else {
    if (isAllowedToRefetchCache(request)) {
      cachedRequests = await fetchRedirects()
    }
  }

  const redirect = cachedRequests[request.nextUrl.pathname]
  if (redirect) return NextResponse.redirect(new URL(redirect.to))

  // ...
}


... and is this what you're suggesting?

// app/cached-redirects-action.ts
'use server'
 
const cachedRedirects = null;

export async function getCachedRedirects(request) {
  const fetchRedirects = async () => {
    const res = await (
      await fetch(`..../redirects`)
    ).json()
    return res['redirects']
  }

  if (!cachedRedirects) {
    cachedRedirects = await fetchRedirects()
  } else {
    if (isAllowedToRefetchCache(request)) {
      cachedRedirects = await fetchRedirects()
    }
  }

  return cachedRedirects;
}

// middleware.ts

export default async function middleware(request) {
  const redirects = await getCachedRedirects(request)
  const redirect = redirects[request.nextUrl.pathname]
  if (redirect) return NextResponse.redirect(new URL(redirect.to))

  // ...
}


I'm not sure on the implications of calling server actions from middleware
Avatar
Arinji
wel there is a reason why you cant cache middleware requests @Silver Fox
its a usually bad idea
to do any sort of long operation in the middleware
but if you need to do it..yes go for a server action
youu can use api routes.. i just like calling a function lol
Avatar
joulev
Nextjs cache doesn’t work there. But since you are self hosting, I suppose you can simply cache in the global object.

Like if global.redirectRules doesn’t exist, then fetch the rules, then save the rules into global.redirectRules. If the global variable already exists, use it.

You can also route requests to be redirected to a route handler, where cache works, then redirect from that route handler.

Server actions are irrelevant here. Do not use them where you don’t need them.
Answer
Avatar
joulev
Also... don't fetch your own api routes in middleware, except in rare cases where there are no alternatives
Avatar
Arinji
That's why I usually go the server action route if it's absolutely necessary to make a cached request in the middleware
With route handlers you gotta worry abt urls and stuff
Actions have good dx lol, but it is very very scuffed doing a post request lmao
Avatar
joulev
server actions are absolutely irrelevant here. middleware is already on the server, so server actions do nothing.

you remember my network boundary tests? these ones https://discord.com/channels/752553802359505017/752647196419031042/1235188655404879893. if you conduct this test in middleware, you will find that server actions are simply run as normal functions in middleware. the use server directive contributes nothing.

cache doesn't work in middleware, so cache doesn't work in server actions either, considering the above.

there are many cases where server actions cannot replace route handlers. this is one of them.
Avatar
joulev
Ping me + use /remove-answer if that’s not the answer