Route Handler not statically generated on Vercel
Unanswered
Singapura posted this in #help-forum
SingapuraOP
I'm trying to statically render some JSON from a Route Handler, but this is not working as expected. I am expecting to see a cache
I also threw in a
I'm using Next.js 14.2.7 and deploying on Vercel.
Am I thinking about this in the wrong way? My mental model was that this would be like ISR pages, generated statiaclly at build time, and then I can perform on-demand revalidations as necessary.
HIT for all requests to /api/hello but I see a lot of MISS and STALE results.I also threw in a
console.log to see if the the function would run, and this will periodically happen.I'm using Next.js 14.2.7 and deploying on Vercel.
Am I thinking about this in the wrong way? My mental model was that this would be like ISR pages, generated statiaclly at build time, and then I can perform on-demand revalidations as necessary.
import { NextRequest, NextResponse } from 'next/server'
export const dynamic = 'force-static'
export const revalidate = false
export function generateStaticParams() {
return [
{ params: { slug: 'hello' } },
{ params: { slug: 'world' } }
]
}
export function GET(_req: NextRequest, { params }: { params: { slug: string } }) {
console.log(`running ${params.slug}`)
return NextResponse.json({
static: true,
slug: params.slug
})
}56 Replies
SingapuraOP
here's what the Deployment summary shows
and here is sampling from the logs, showing that this runs multiple times
@Singapura I'm trying to statically render some JSON from a Route Handler, but this is not working as expected. I am expecting to see a cache `HIT` for all requests to `/api/hello` but I see a lot of `MISS` and `STALE` results.
I also threw in a `console.log` to see if the the function would run, and this will periodically happen.
I'm using Next.js 14.2.7 and deploying on Vercel.
Am I thinking about this in the wrong way? My mental model was that this would be like ISR pages, generated statiaclly at build time, and then I can perform on-demand revalidations as necessary.
ts
import { NextRequest, NextResponse } from 'next/server'
export const dynamic = 'force-static'
export const revalidate = false
export function generateStaticParams() {
return [
{ params: { slug: 'hello' } },
{ params: { slug: 'world' } }
]
}
export function GET(_req: NextRequest, { params }: { params: { slug: string } }) {
console.log(`running ${params.slug}`)
return NextResponse.json({
static: true,
slug: params.slug
})
}
generateStaticParams should've beenexport function generateStaticParams() {
return [
{ slug: 'hello' },
{ slug: 'world' },
];
}SingapuraOP
thanks. i’ve done this, but still running into issues when deploying on vercel
Masai Lion
@Singapuradid you check the error in vecel?
SingapuraOP
the issue now is that on-demand paths/params do not seem to be cached statically. i've made a demo to show what i mean.
* https://next-static-output.vercel.app/api/hello
* https://next-static-output.vercel.app/api/foo
here's the code for this route on github.
https://github.com/kupppo/next-static-output/blob/main/app/api/%5Bslug%5D/route.ts
what i expected to see was after
* https://next-static-output.vercel.app/api/hello
* https://next-static-output.vercel.app/api/foo
here's the code for this route on github.
https://github.com/kupppo/next-static-output/blob/main/app/api/%5Bslug%5D/route.ts
/api/hello is generated at build time via generateStaticParams and always returns a 304. i also don't see the request path in my Vercel logs, indicating it's a Static Request. i can spam requests to this URL as many times as I want and my console.log in the function only runs at build-time./api/foo is generated on-demand and always returns a 200. i see this in my Vercel logs for every visit, although the function is not run every request because some of the Cache headers are STALE and MISS.what i expected to see was after
/api/foo is generated, it would be cached statically and it would behave identically to /api/hello. i would expect to never see the console.log from the function being called again as it would only regenerate via a revalidatePath call.i've built and run my application locally with the NEXT_PRIVATE_DEBUG_CACHE=1 environment variable and this runs as expected. at build time, both /api/hello and /api/world have generated their respective .body and .meta files withing the .next/server/app folder. i'm consistently getting a HIT, 200 status, and not seeing the console.log from the function being run.
when i visit /api/foo, the first visit results in a MISS, but still a 200 status and the function is run as expected. each subsequent visit has a HIT, 200 status, not seeing the function being run (all expected). i can also see that foo.body and foo.meta are in the .next/server/app folder.
i've also deployed to Vercel, once with the NEXT_PRIVATE_DEBUG_CACHE=1 environment variable and once without. when i deploy without, the /api/foo will continuously return a 200, but i often see STALE in the header. in the logs, i also see a fair amount of MISS requests as well. the function runs many times.
when deployed to Vercel with the NEXT_PRIVATE_DEBUG_CACHE=1 environment variable, the function never appears shows the console.log from running. however, it always responsds with a 200 and STALE. this also shows up in the logs.
when i visit /api/foo, the first visit results in a MISS, but still a 200 status and the function is run as expected. each subsequent visit has a HIT, 200 status, not seeing the function being run (all expected). i can also see that foo.body and foo.meta are in the .next/server/app folder.
i've also deployed to Vercel, once with the NEXT_PRIVATE_DEBUG_CACHE=1 environment variable and once without. when i deploy without, the /api/foo will continuously return a 200, but i often see STALE in the header. in the logs, i also see a fair amount of MISS requests as well. the function runs many times.
when deployed to Vercel with the NEXT_PRIVATE_DEBUG_CACHE=1 environment variable, the function never appears shows the console.log from running. however, it always responsds with a 200 and STALE. this also shows up in the logs.
using fetch cache handler
using cache endpoint https://iad1.suspense-cache.vercel-infra.com
running foo
using filesystem cache handler
using fetch cache handler
using cache endpoint https://iad1.suspense-cache.vercel-infra.comso i’m not seeing an “error” on vercel, but it’s definitely not the expected behavior
Masai Lion
is this your project?
SingapuraOP
it’s here: https://github.com/kupppo/next-static-output
Masai Lion
We're talking about the current project's deployment failure. right?
SingapuraOP
no, not the deployment failure
Masai Lion
?
what?
SingapuraOP
if you look above at what i posted, that outlines how the cache behavior is not as expected once i deploy to vercel
Masai Lion
i understood.
SingapuraOP
i don’t see a deployment failure anywhere actually
Masai Lion
could you share frontend code?
for api/foo
SingapuraOP
yeah, it’s on the link i posted above
there’s the link to the exact route.
hello and world are handled at build time via generateStaticParamsso when i call
/api/hello, it’s completely static. the console.log in my function does not runi would expect that
* generates the response by running the route handler (ie console.log shows up)
* the response is cached the exact same way
/api/foo, which is not statically generated at build time, does the following:* generates the response by running the route handler (ie console.log shows up)
* the response is cached the exact same way
/api/hello iswhat ends up happening is that
/api/foo consistently returns a STALE cache header and i see the function being run in my vercel logs. the console.log appears more than once. i’d guesstimate once a minuteMasai Lion
import { NextRequest, NextResponse } from 'next/server'
export const dynamic = 'force-static'
export const revalidate = false
export async function generateStaticParams() {
return [
{ slug: 'hello' },
{ slug: 'world' }
]
}
export function GET(_req: NextRequest, { params }: { params: { slug: string } }) {
console.log(`running ${params.slug}`)
return NextResponse.json({
static: true,
slug: params.slug
})
}SingapuraOP
yep, that’s the function. i tried to be as explicit as possible. for example,
revalidate is false, which is also the defaultMasai Lion
return NextResponse.json(
{ static: false, slug: params.slug },
{
headers: {
'Cache-Control': 's-maxage=60, stale-while-revalidate=59',
},
}
);I think you can set headers.
SingapuraOP
i can do that, but that’s not the same thing as ISR
i’ve done that in other projects, but it’s not the same result
Masai Lion
not the same result?
sorry but how many years do u have experience do you have with next.js?
SingapuraOP
several
i’ve been doing web development for 20+ years
Masai Lion
oh~ nice. are you experienced with Google Analytics?
@Singapura
SingapuraOP
yep, feel free to DM about anything analytics
with regards to the cache header, Vercel often treats those as “best effort” and will eject them as they see fit
whereas the actual static part never gets ejected
Masai Lion
@Singapura Thank you.
Have you tried setting
I think you can find the error clearly.
Have you tried setting
revalidate = false to revalidate = 60 and testing it? I think you can find the error clearly.
SingapuraOP
i’ll give that a go as a test, but i really want to only run my function revalidation on demand
as opposed to doing it on timed interval
is there a reason that revalidate = false would not work? from the docs and experience, i would think should work
so revalidate = false doesn't work but removing revalidate does? sounds like a bug
interesting bug
SingapuraOP
i’ll check with revalidate in a timed interval later, i’m away from the computer at the moment
i’m also going to build a little UI for this to demo instead of pasting this enormous wall of text
SingapuraOP
i've pushed a time-based revalidate route.
https://github.com/kupppo/next-static-output/blob/main/app/api/%5Bslug%5D/interval/route.ts
https://github.com/kupppo/next-static-output/blob/main/app/api/%5Bslug%5D/interval/route.ts
will report back on how this works once delpoyed to vercel
SingapuraOP
here is how
https://next-static-output.vercel.app/api/foo/interval runs. it's definitely re-generating more than once a minute./api/home/interval is also generating more than once a minute/api/hello/interval is generating at most once a minuteSingapuraOP
https://next-static-output.vercel.app/api/hello on the other hand, which is generated at build time, literally shows no logs. that's expected because static requests are not available in the runtime logs.https://vercel.com/docs/observability/runtime-logs#available-log-types
SingapuraOP
so it seems like the big issue here is that regardless of whether it's a timed interval on on-demand, the
fallback params never populate into the static build on Vercel, but they do when built locallySingapuraOP
i've made a demo to showcase how this works.
https://next-static-output.vercel.app/
https://next-static-output.vercel.app/