admin管理员组文章数量:1390447
I have a Next.js application with a dynamic page for displaying resources: /resource/[id]
. Whenever a user edits (say) resource #5, I want to regenerate the page /resource/5
in Next's cache.
I have an API route (in my /pages
directory) that handles the resource editing. From what I've read, I should be able to make it refresh the display route by doing:
response.revalidate(`/resource/${id}/`);
However, that doesn't work; I get the error:
Error: Failed to revalidate /resource/2153/: Invalid response 200
at revalidate (/home/jeremy/GoblinCrafted/next/node_modules/next/dist/server/api-utils/node.js:388:15)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
- error unhandledRejection: Error: Failed to revalidate /resource/2153/: Invalid response 200
at revalidate (/home/jeremy/GoblinCrafted/next/node_modules/next/dist/server/api-utils/node.js:388:15)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
digest: undefined
I guess Next is trying to revalidate through an HTTP request, but the revalidation endpoint moved? I'm not exactly sure why this fails.
EDIT: I dug into the Next source code, and it seems like revalidate
is just making a mock request to the route that is being revalidated. If you look at node_modules/next/dist/server/router.js
you'll see:
async revalidate({ urlPath , revalidateHeaders , opts }) {
const mocked = (0, _mockrequest.createRequestResponseMocks)({
url: urlPath,
headers: revalidateHeaders
});
const handler = this.getRequestHandler();
await handler(new _node.NodeNextRequest(mocked.req), new _node.NodeNextResponse(mocked.res));
await mocked.res.hasStreamed;
if (mocked.res.getHeader("x-nextjs-cache") !== "REVALIDATED" && !(mocked.res.statusCode === 404 && opts.unstable_onlyGenerated)) {
throw new Error(`Invalid response ${mocked.res.statusCode}`);
}
My error is ing from that throw
at the end ... but that doesn't make any sense to me, because when I log urlPath
, it's just the path I'm trying to refresh (eg. /resource/5
). When I try to hit that path with a GET request (either in my browser or through Postman) I don't get a redirect: I get a 200.
This led me to try adding a /
to the end of my path:
response.revalidate(`/resource/${id}/`);
That got me a similar error, only this time it was for a 200 instead of a 308:
Error: Invalid response 200
So, it seems my status code doesn't actually matter: it's the mocked.res.getHeader("x-nextjs-cache") !== "REVALIDATED"
part that's the problem. However, digging through the code, I only found one place that sets that header. It happens within a function within a function within a renderToResponseWithComponentsImpl
function in node_modules/next/dist/esm/server/base-server.js
:
if (isSSG && !this.minimalMode) {
// set x-nextjs-cache header to match the header
// we set for the image-optimizer
res.setHeader("x-nextjs-cache", isOnDemandRevalidate ? "REVALIDATED" : cacheEntry.isMiss ? "MISS" : cacheEntry.isStale ? "STALE" : "HIT");
}
... but when I add console.log
statements it seems that code isn't being reached ... even though I see lots of other requests reaching it, and isSSG && !this.minimalMode
is true for all of them.
So, in short, somehow the mock request revalidate
makes isn't triggering the setting of that header, which then makes it fail ... but I have no clue why it's not getting to that header-setting code, because it's so deeply nested in the router code.
END EDIT
I also tried using revalidatePath
, from next/cache
:
revalidatePath(`/resource/[id]`);
but that also gives an error:
revalidatePath(`/resource/[id]`);
Error: Invariant: static generation store missing in revalidateTag /resource/[id]
at revalidateTag (/home/me/project/next/node_modules/next/dist/server/web/spec-extension/revalidate-tag.js:15:15)
at revalidatePath (/home/me/project/next/node_modules/next/dist/server/web/spec-extension/revalidate-path.js:13:45)
at handler (webpack-internal:///(api)/./pages/api/resource.js:88:67)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
I think this is because revalidatePath
is only intended to be used in the /app
directory?
Finally, I found a reference to a response.unstable_revalidate
method, which seemed to be designed for revalidating dynamic paths:
response.unstable_revalidate(`/resource/${id}/`);
... but when I try to use it, it isn't there on the response:
TypeError: response.unstable_revalidate is not a function
at handler (webpack-internal:///(api)/./pages/api/resource.js:88:18)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
I have a Next.js application with a dynamic page for displaying resources: /resource/[id]
. Whenever a user edits (say) resource #5, I want to regenerate the page /resource/5
in Next's cache.
I have an API route (in my /pages
directory) that handles the resource editing. From what I've read, I should be able to make it refresh the display route by doing:
response.revalidate(`/resource/${id}/`);
However, that doesn't work; I get the error:
Error: Failed to revalidate /resource/2153/: Invalid response 200
at revalidate (/home/jeremy/GoblinCrafted/next/node_modules/next/dist/server/api-utils/node.js:388:15)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
- error unhandledRejection: Error: Failed to revalidate /resource/2153/: Invalid response 200
at revalidate (/home/jeremy/GoblinCrafted/next/node_modules/next/dist/server/api-utils/node.js:388:15)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
digest: undefined
I guess Next is trying to revalidate through an HTTP request, but the revalidation endpoint moved? I'm not exactly sure why this fails.
EDIT: I dug into the Next source code, and it seems like revalidate
is just making a mock request to the route that is being revalidated. If you look at node_modules/next/dist/server/router.js
you'll see:
async revalidate({ urlPath , revalidateHeaders , opts }) {
const mocked = (0, _mockrequest.createRequestResponseMocks)({
url: urlPath,
headers: revalidateHeaders
});
const handler = this.getRequestHandler();
await handler(new _node.NodeNextRequest(mocked.req), new _node.NodeNextResponse(mocked.res));
await mocked.res.hasStreamed;
if (mocked.res.getHeader("x-nextjs-cache") !== "REVALIDATED" && !(mocked.res.statusCode === 404 && opts.unstable_onlyGenerated)) {
throw new Error(`Invalid response ${mocked.res.statusCode}`);
}
My error is ing from that throw
at the end ... but that doesn't make any sense to me, because when I log urlPath
, it's just the path I'm trying to refresh (eg. /resource/5
). When I try to hit that path with a GET request (either in my browser or through Postman) I don't get a redirect: I get a 200.
This led me to try adding a /
to the end of my path:
response.revalidate(`/resource/${id}/`);
That got me a similar error, only this time it was for a 200 instead of a 308:
Error: Invalid response 200
So, it seems my status code doesn't actually matter: it's the mocked.res.getHeader("x-nextjs-cache") !== "REVALIDATED"
part that's the problem. However, digging through the code, I only found one place that sets that header. It happens within a function within a function within a renderToResponseWithComponentsImpl
function in node_modules/next/dist/esm/server/base-server.js
:
if (isSSG && !this.minimalMode) {
// set x-nextjs-cache header to match the header
// we set for the image-optimizer
res.setHeader("x-nextjs-cache", isOnDemandRevalidate ? "REVALIDATED" : cacheEntry.isMiss ? "MISS" : cacheEntry.isStale ? "STALE" : "HIT");
}
... but when I add console.log
statements it seems that code isn't being reached ... even though I see lots of other requests reaching it, and isSSG && !this.minimalMode
is true for all of them.
So, in short, somehow the mock request revalidate
makes isn't triggering the setting of that header, which then makes it fail ... but I have no clue why it's not getting to that header-setting code, because it's so deeply nested in the router code.
END EDIT
I also tried using revalidatePath
, from next/cache
:
revalidatePath(`/resource/[id]`);
but that also gives an error:
revalidatePath(`/resource/[id]`);
Error: Invariant: static generation store missing in revalidateTag /resource/[id]
at revalidateTag (/home/me/project/next/node_modules/next/dist/server/web/spec-extension/revalidate-tag.js:15:15)
at revalidatePath (/home/me/project/next/node_modules/next/dist/server/web/spec-extension/revalidate-path.js:13:45)
at handler (webpack-internal:///(api)/./pages/api/resource.js:88:67)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
I think this is because revalidatePath
is only intended to be used in the /app
directory?
Finally, I found a reference to a response.unstable_revalidate
method, which seemed to be designed for revalidating dynamic paths:
response.unstable_revalidate(`/resource/${id}/`);
... but when I try to use it, it isn't there on the response:
TypeError: response.unstable_revalidate is not a function
at handler (webpack-internal:///(api)/./pages/api/resource.js:88:18)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Share
Improve this question
edited Aug 25, 2023 at 0:08
Drew Reese
204k18 gold badges245 silver badges273 bronze badges
asked Aug 15, 2023 at 22:23
machineghostmachineghost
35.8k32 gold badges173 silver badges260 bronze badges
18
-
What is your Next.js version? Also, can you double-check you are calling
revalidate
with the correct path? The error message suggests you are trying to revalidate/resource/2153/edit
instead of/resource/2153
-Error: Failed to revalidate /resource/2153/edit
– Igor Danchenko Commented Aug 21, 2023 at 19:20 -
13.4.16. And yeah, I was also trying to revalidate
/resource/5/edit
at the same time, but I was trying to keep the post simple by not mentioning the second route (and then I guess I accidentally copied the stack trace from the edit revalidate instead of the/resource/5
one). Thank you for catching that. I've fixed the stack trace, and noticed that for the/resource/5
one is failing because of a 200 (not a 308 like the/edit
)... which makes even less sense to me :( – machineghost Commented Aug 21, 2023 at 20:22 -
I just put together a quick test, and as I can see
revalidate
is working as expected. Do you think you can provide a minimal reproducible example? You can use the following working example as a template - codesandbox.io/p/sandbox/zen-wright-83kxjv?file=/pages/resource/… – Igor Danchenko Commented Aug 21, 2023 at 21:31 - I tweaked your repro to create codesandbox.io/p/sandbox/stupefied-flower-hj86y3?file=/pages/…. It fails to update the timestamp, but it doesn't cause the error that I'm seeing, so I'll try to contrast my code with your simpler version and see if I can find the cause. – machineghost Commented Aug 22, 2023 at 1:37
-
So are you using Pages Router (
/pages
) or App Router (/app
)? Based on your original question I got the impression that you are using legacy pages router (/pages
). But if your pages actually reside under the app router, you should use the the newrevalidatePath
/revalidateTag
API and move your API route handler under the/app
as well | nextjs/docs/app/api-reference/functions/revalidatePath – Igor Danchenko Commented Aug 22, 2023 at 2:06
3 Answers
Reset to default 11st option, on your getStaticPaths
function, does it return
`return { paths, fallback: 'blocking' }`
On Next JS documentation: { fallback: 'blocking' } will server-render pages on-demand if the path doesn't exist.
2nd option, on your getStaticProps
function, try to omit revalidate: true
or at least set it to false
.
Next js Documentation: If revalidate is omitted, Next.js will use the default value of false (no revalidation) and only revalidate the page on-demand when revalidate() is called.
Check out the Next.js docs here - https://nextjs/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#on-demand-revalidation
You need to use revalidatePath
or revalidateTag
in Next.js 13 app router to revalidate based on path or tag respectively.
It looks like res.revalidate
is the correct approach, and I'm just running into some sort of bug. I've filed an issue with the Vercel team at https://github./vercel/next.js/issues/54420, an am awaiting a response.
本文标签: javascriptHow Can I Manually Revalidate a Dynamic Nextjs RouteFrom an API RouteStack Overflow
版权声明:本文标题:javascript - How Can I Manually Revalidate a Dynamic Next.js Route, From an API Route? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744578356a2613765.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论