Cache fetch requests in middleware?
Answered
Silver Fox posted this in #help-forum
![Avatar](https://cdn.discordapp.com/embed/avatars/3.png)
Silver FoxOP
Can you cache fetch requests in middleware.ts?
e.g. from an external route
e.g. from a next.js route
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.
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.
22 Replies
![Avatar](https://cdn.discordapp.com/avatars/194128415954173952/708a8637ada505b6b7a85972807d9dca.webp?size=256)
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](https://cdn.discordapp.com/embed/avatars/3.png)
Silver FoxOP
Ty for the response!
In vercelWe 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 handlersWe 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](https://cdn.discordapp.com/avatars/194128415954173952/708a8637ada505b6b7a85972807d9dca.webp?size=256)
Alfonsus Ardani
hmm
thats a solid use case
![Avatar](https://cdn.discordapp.com/avatars/890486507872342027/cba9267c0173c6acfde7fa4ba0e70f94.webp?size=256)
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](https://cdn.discordapp.com/embed/avatars/3.png)
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...
... and is this what you're suggesting?
I'm not sure on the implications of calling server actions from middleware
// 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](https://cdn.discordapp.com/avatars/890486507872342027/cba9267c0173c6acfde7fa4ba0e70f94.webp?size=256)
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](https://cdn.discordapp.com/avatars/484037068239142956/44415017de6fed2ccd5d44b4b2051ac0.webp?size=256)
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.
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](https://cdn.discordapp.com/avatars/484037068239142956/44415017de6fed2ccd5d44b4b2051ac0.webp?size=256)
joulev
Also... don't fetch your own api routes in middleware, except in rare cases where there are no alternatives
![Avatar](https://cdn.discordapp.com/avatars/890486507872342027/cba9267c0173c6acfde7fa4ba0e70f94.webp?size=256)
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](https://cdn.discordapp.com/avatars/484037068239142956/44415017de6fed2ccd5d44b4b2051ac0.webp?size=256)
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
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.
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](https://cdn.discordapp.com/avatars/484037068239142956/44415017de6fed2ccd5d44b4b2051ac0.webp?size=256)
joulev
Ping me + use /remove-answer if that’s not the answer