vercel / next.js

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

[Needs reproduction] Revalidation issues with the App Router #49417

Closed leerob closed 1 year ago

leerob commented 1 year ago

Moved from https://github.com/vercel/next.js/discussions/42290

Edit: Updated Reply


Hey folks, small update here. Since this issue has been posted, we've fixed a number of bugs with revalidation in the App Router. Further, we have published brand new caching documentation that goes in depth on fetching, caching, and revalidating:

Going forward, we can't look into any issues mentioned unless there's a reproduction provided. If you are still seeing an issue, please create a minimal repro and open a new issue. Thank you! 🙏

mbathie commented 1 year ago

It's also an issue when using fetch with { cache: 'no-store' } setting to fetch data from your local Next API. One work around I found was to make sure you have POST and GET requests stubbed out in your API route.js file. When I only had GET specified it failed to get the most up-to-data data from the API call until I fully rebuilt the production next app (npm run build ; npm run start)

leonace924 commented 1 year ago

This doesn't work for me either, I used v13.3.4 and used GraphQL When the article is added in backend, it doesn't reflect on the front-end before redeploy

DeabitTech commented 1 year ago

@leerob hi, it is very important fix this issue, because it means that whenever i have any property value changed in my backend, it is not showing in the page, this is a very big problem. I saw that you need reproduction code, i want help you and the teams to fix quickly this issue, so tell me what do you need, please.

Kindly regards

DeabitTech commented 1 year ago

It's also an issue when using fetch with { cache: 'no-store' } setting to fetch data from your local Next API. One work around I found was to make sure you have POST and GET requests stubbed out in your API route.js file. When I only had GET specified it failed to get the most up-to-data data from the API call until I fully rebuilt the production next app (npm run build ; npm run start)

so you mean that it's necessary change the name of the function inside the route.js from GET to POST also when it's not necessary catch params in input?

DeabitTech commented 1 year ago

@leerob here is my case:

I have just one next app created by create-next-app with next:13.3.4.

I used App Router and i have a simple Home page, below my code:

`import { Playfair_Display } from 'next/font/google'
import HeroSection from './components/HeroSection'
import WhySection from './components/WhySection'
import CountsMatterSection from './components/CountsMatterSection'
import TestimonalsSection from './components/TestimonalsSection'
import ContactSection from './components/ContactSection'
import { CountsMatters, Hero, Why } from '@/typings'
export const revalidate = 5;
const inter = Playfair_Display({ subsets: ['latin'] })

export default async function Home() {
  const heroInfo:Hero[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getHero`,{next:{revalidate:5}})).json();
  const whyInfo:Why[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getWhy`,{next:{revalidate:5}})).json();
  const countsInfo:CountsMatters[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getCounts`,{next:{revalidate:5}})).json();

  return (
    <main className={`${inter.className} min-h-screen dark:bg-slate-700 bg-slate-100`}>
      <section>
        <HeroSection heroInfo={heroInfo[0]}/>
      </section>
      <section>
        <WhySection whyInfo={whyInfo[0]}/>
      </section>
      <section>
        <CountsMatterSection countsInfo={countsInfo[0]}/>
      </section>
      <section>
        <TestimonalsSection/>

      </section>
      <section>
        <ContactSection/>
      </section>
    </main>
  )
}`

and also there's my simply route.ts:

`import { NextResponse,NextRequest } from "next/server"
import { groq } from "next-sanity"
import { sanityClient } from "@/sanity"
import {CountsMatters} from "@/typings"

const query = groq`
    *[_type=="countsMatters"]

`

export async function GET(request: NextRequest) {
  const countInfo :CountsMatters[] = await sanityClient.fetch(query).catch(e=>console.warn(e));
  return  NextResponse.json(countInfo)
}
`

here also my sanityClient:

`import { createClient } from "next-sanity";
import imageUrlBuilder from "@sanity/image-url";

export const config = {
    dataset:process.env.NEXT_SANITY_DATASET || "production",
    projectId: process.env.NEXT_PROJECT_ID,
    apiVersion: '2021-08-31',
    useCdn: false
}

export const sanityClient = createClient(config); 
const builder = imageUrlBuilder(sanityClient)

export const urlFor = (source:any) => builder.image(source); `

as your documentation says i set useCdn:false, but not working. for me it is just a broblem with the self API exposed, because if i write one usual function with the query by sanity client i can catch the changes quickly, but in order to use the built-in feature, i think it could be better use the self API. I have this behavior also when i call other external endpoints using fetch, maybe is this the origin of the problem

In the enviroment variable: process.env.NEXT_PUBLIC_BASE_URL i put my vercel url instance, because as you know, if i leave the localhost url when i will go to the build by vercel, i will find an error related to unreachable endpoint.

Please help us with a solution if the code is wrong or a fix.

Kindly regards

bitstorm-tech commented 1 year ago

I also made a little project to reproduce the behavior: Project

Unfortunately, it is not working on CodeSandbox, but at least you can see the code or download and run it locally.

How to reproduce:

  1. click on "List Posts"
  2. click on "Add Posts"
  3. click on ">> Add Posts <<"
  4. click on "List Posts"

Expected behavior: You see at least 3 posts on the "List Posts" page.

Actual behavior: You only see the initial two posts (the "List Posts" page is not reloaded also the revalidation is set to 0)

vainia commented 1 year ago

Same issue here. When running locally cache revalidation works but then once deployed to Vercel data seems to be fetched only single time via Prisma API and persisted forever even though revalidate is predefined as 10 (copied from an example). I have recently changed my connection config for Prisma to try out Vercel Postgres (previously used Supabase)

vainia commented 1 year ago

Same issue here. When running locally cache revalidation works but then once deployed to Vercel data fetched once from API and persisted forever even though revalidate is predefined as 10 (copied from an example). I have recently changed my connection config for Prisma to try out Vercel Postgres (previously used Supabase)

image

mbathie commented 1 year ago

It's also an issue when using fetch with { cache: 'no-store' } setting to fetch data from your local Next API. One work around I found was to make sure you have POST and GET requests stubbed out in your API route.js file. When I only had GET specified it failed to get the most up-to-data data from the API call until I fully rebuilt the production next app (npm run build ; npm run start)

so you mean that it's necessary change the name of the function inside the route.js from GET to POST also when it's not necessary catch params in input?

No, just need to have both specified. I found if I only have POST in the route.js, it didn't return the most recent data until I specified GET as well. The GET function doesn't need to do anything, it just needs to be stubbed.

i.e.

export async function POST(request) {
  return NextResponse.json(latestData)
}

export async function GET(request) {
  return NextResponse.json({})
}
leerob commented 1 year ago

@vainia that code example is using getStaticProps and pages/. This is for the App Router (app/).

leerob commented 1 year ago

@DeabitTech you don't need the API routes anymore, you can call the code directly. Also, fetch the data in parallel:

Old

const heroInfo:Hero[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getHero`,{next:{revalidate:5}})).json();
const whyInfo:Why[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getWhy`,{next:{revalidate:5}})).json();
const countsInfo:CountsMatters[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getCounts`,{next:{revalidate:5}})).json();

New

// app/page.js
export const revalidate = 5;

export default async function Page() {
    const [heroInfo, whyInfo, countsInfo] = await Promise.all([
        getHero(),
        getWhy(),
        getCounts()
    ])

    // ...
}
DeabitTech commented 1 year ago

@DeabitTech you don't need the API routes anymore, you can call the code directly. Also, fetch the data in parallel:

Old

const heroInfo:Hero[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getHero`,{next:{revalidate:5}})).json();
const whyInfo:Why[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getWhy`,{next:{revalidate:5}})).json();
const countsInfo:CountsMatters[] = await (await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/getCounts`,{next:{revalidate:5}})).json();

New

// app/page.js
export const revalidate = 5;

export default async function Page() {
    const [heroInfo, whyInfo, countsInfo] = await Promise.all([
        getHero(),
        getWhy(),
        getCounts()
    ])

    // ...
}

Yeah it's true, because I'm using the client of sanity and i can set a simply functions and call them in the page.

But the problem is not just when i use the API routes.

I have a simply request using fetch to YouTube API for retrive a playlist items.

If i add new items to the playlist, in production i can't see them.

So in order to see them in production i need take a build again.

Also in this case i used revalidate property in the fetch options.

Anyway i don't think that the solution is just avoid the behavior you can have with the API routes. I know that the vercel team working right now to the server function and i guess that will be a right way instead of API routes.

But in the meantime still a problem in general with revalidation using API routes and also external endpoint using fetch

epndavis commented 1 year ago

I've created a repository

Envrionment

      Platform: win32
      Arch: x64
      Version: Windows 10 Home
    Binaries:
      Node: 16.14.2
      npm: N/A
      Yarn: N/A
      pnpm: N/A
    Relevant packages:
      next: 13.4.1
      eslint-config-next: 13.4.1
      react: 18.2.0
      react-dom: 18.2.0

To Reproduce

  1. Load the homepage
  2. Navigate to /dataApi
  3. Wait 20 seconds for the graphql endpoint to revalidate
  4. Refresh the homepage, then refresh again to get the new data.
  5. Refresh dataApi
  6. Call the revalidate endpoint /api/revalidate?tag=data
  7. Refresh homepage again, then again to get new data

Describe the bug Though we have two fetch requests on the page, only one has a revalidate. When the homepage is refreshed both fetches get new data, then data for the same fetch between dataApi and homepage are different. If revalidated by the tag for the restApi fetch request, reload the homepage both sets of data are updated.

Expected Behaviour Going off what was described in this youtube video we would expect the restApi data to be static and not fetched again when the graphql endpoint is revalidated. When revalidated by tag for the data api we would expect the graphql endpoint not to be revalidated based on the fetch-cache data in the .next folder.

When revalidating a tag for example if that fetch is used in the layout for every page, it then fetches again for every page., though I have found it difficult to understand from the docs how the fetch works across pages. I don't know if I'm implementing it incorrectly

vainia commented 1 year ago

@vainia that code example is using getStaticProps and pages/. This is for the App Router (app/).

Thank you for the reply @leerob! I have now updated my code to use getServerSideProps. However, after surfing over my bookmarks of Vercel documentation guides for Next.js I stumbled upon this link https://vercel.com/docs/concepts/incremental-static-regeneration/quickstart. I believe that's where I initially found getStaticProps. So if I understand you correct it's now a wrong concept (seemingly deprecated since some recent updates) and I can only use getStaticProps in App Router?

[Update] Or did you mean that my issue is not related to the topic of this thread?

karlhorky commented 1 year ago

@vainia yeah, your issue is most likely not related because you're using the pages/ directory. This issue is about the app/ directory (called the "App Router" in the docs).

isomorpheric commented 1 year ago

Moved from #42290

Awaiting issue reproduction.

@leerob I have made a minimal demo and filed it with the issue template. Here is the separate issue: #49571

vxna commented 1 year ago

@leerob @karlhorky I believe the revalidation bug with Pages dir is also worth tracking. App router might be stable but it's not feature complete with Pages and I have a feeling that most of the Next.js users with real-world production apps didn't migrate (or will be migrating in a feasible future) just yet.

I don't have a public shareable repro tho but I can confirm the bug exists there as well. I've tried to reach out for vercel support on a separate thread, but without any success.

@vainia could you please create a separate issue? 🙏

pm0u commented 1 year ago

I ran into what I thought was this issue, but it actually was that my API route was statically Cached. @leerob I think revalidatePath and revalidateTag should be included in dynamic functions that would cause an API route to be SSR at request time. I don't see a scenario where those functions should be excluded... In the previous behavior, before I set dynamic = 'force-dynamic' on my API route, the API route was run and cache was immediately revalidated at build time and then never revalidated when the API route was called. This seems like behavior no one would want?

edit: also, because this particular API route was a cron task I thought my cron was just never running or was for some reason erroring out while running. But since (it seems?) the way vercel runs crons is just to hit an API with a GET request, it was just returning the static cache and never actually running my cron task. At least, thats the best explanation I can infer without deeper knowledge of how Vercel works.

DeabitTech commented 1 year ago

I ran into what I thought was this issue, but it actually was that my API route was statically Cached. @leerob I think revalidatePath and revalidateTag should be included in dynamic functions that would cause an API route to be SSR at request time. I don't see a scenario where those functions should be excluded... In the previous behavior, before I set dynamic = 'force-dynamic' on my API route, the API route was run and cache was immediately revalidated at build time and then never revalidated when the API route was called. This seems like behavior no one would want?

edit: also, because this particular API route was a cron task I thought my cron was just never running or was for some reason erroring out while running. But since (it seems?) the way vercel runs crons is just to hit an API with a GET request, it was just returning the static cache and never actually running my cron task. At least, thats the best explanation I can infer without deeper knowledge of how Vercel works.

Yeah exactly, just in a build time have the changes from the previous to the next data, you look my code just three messages before, you think that if i use revalidatePath i can change the data on running in production?

I tried to get a import of revalidatePath but was wrong

anarkrypto commented 1 year ago

I'm having this error with v13.4.2

export const revalidate = 60; // revalidate this segment every 60 seconds

Error: x Only async functions are allowed to be exported in a "use server" file.

Does that make any sense?

AlexKMarshall commented 1 year ago

I think this is related to the issue I'm seeing.

I have a page that reads from cookies. I expect it to SSR every time I visit it, but stale data is getting cached.

Here's a video demonstrating the issue https://app.claap.io/qogita/dynamic-page-caching-incorrectly-c-n6eMJcyi6K-4Bqmv2HWPHvE And the deployed site: https://next-cache-issue.vercel.app/ And a minimal reproduction repo https://github.com/AlexKMarshall/next-cache-issue

epndavis commented 1 year ago

I have submitted a new issue from the one I made here using the latest release. https://github.com/vercel/next.js/issues/49778

ijjk commented 1 year ago

@epndavis responded on the separate issue here https://github.com/vercel/next.js/issues/49778#issuecomment-1546968234 showing correct behavior

@AlexKMarshall this appears to be a client router caching issue with server actions and the revalidations itself are successful, please open a separate issue for tracking cc @feedthejim for visibility

I'm going to close this out so any new issues that come up related to revalidateTag, revalidatePath, etc have their own issue with reproductions.

karlhorky commented 1 year ago

Wonder if NEXT-1128 (#49450) has anything to do with @AlexKMarshall's issue - there are some similarities.

DeabitTech commented 1 year ago

@ijjk no sorry why you close this issue, i have this issue about the revalidation also if i use the method of @leerob using a simply function instread of api routes, but in production when i change the data in my CMS, nothing happens. please this is a big problem if is not possible have the changes from the backend to the production up to date.

I don't know if you understant what i mean, it's very big problem...

Kindly regards

jaj232 commented 1 year ago

@leerob The revalidate { next: { revalidate: 10 } } option is not working as expected on localhost for me. It is working correctly here https://codesandbox.io/p/sandbox/nifty-easley-g1op2q, but not if I run the same code on localhost(tried both npm run dev, and npm run build, npm run start). The code is the same, so I'm not sure why its not revalidating correctly on localhost. codesandbox_is_revalidating localhost_not_revalidating

ijjk commented 1 year ago

@DeabitTech please open a separate issue with reproduction and we can investigate your case individually! This is closed to ensure new bug reports are opened individually with their own reproductions for tracking.

ijjk commented 1 year ago

@jaj232 it looks like your issue may be related to https://github.com/vercel/next.js/issues/49450 so please follow that thread for tracking

jwalcher commented 1 year ago

I'm finding revalidation issues with both pages and app directories. res.revalidate() throws an Error 500 when called on a path that's rewritten by middleware (this is so roughly since 13.3.2-canary.7, and mentioned in https://github.com/vercel/next.js/issues/48948). revalidatePath does not work on routes created with generatedStaticParams(). I opened https://github.com/vercel/next.js/issues/49955 for this, reproduction here

@ijjk is there any way to consolidate all these revalidation issues and threads.

ijjk commented 1 year ago

throws an Error 500 when called on a path that's rewritten by middleware

This isn't supported, see related docs here that mention middleware is not invoked for On-Demand Revalidate requests. This didn't change recently.

revalidatePath is currently not expected to work for request pathnames only the path on in the app directory e.g. /blog/[slug] not /blog/first, we are investigating the later and that can be tracked in https://github.com/vercel/next.js/issues/49387#issuecomment-1552394378

https://github.com/vercel/next.js/issues/48948 should be fixed in latest version v13.4.3.

reza-ebrahimi commented 1 year ago

Not working on next 13.4.2:

export const dynamic = 'force-static'; // not working with or without this line
export const revalidate = 30;

UPDATE:

I figured out this is the correct behavior. Because nextjs generates and copies the generated files into the .next folder on revalidation cycles and won't notify the client to retrieve the fresh html page. So user should refresh the page to see the latest (revalidated) changes.

jwalcher commented 1 year ago

@ijjk Thanks for clarification regarding revalidatePath, but I'm sorry res.revalidate really has a bug.

I am indeed not trying to revalidate any path through middleware.

Rather, what seems to happen is that when I call res.revalidate on a path that is the actual route (as per the docs), but that middleware would try to rewrite because of some wildcard matcher config, middleware fails, and then res.revalidate throws the Error 500. If I disable middleware for the path on which I am calling revalidate, the error disappears. But this strategy is not feasible in general if all these routes are generated dynamically.

My code works up to and including 13.3.2-canary.6 and fails for all versions after that. https://github.com/vercel/next.js/issues/48948 helped a bit, but the main error is still there in most recent version. I'd be happy to make another minimal reproduction if that helps.

danielschmitz commented 1 year ago

I'm using 13.4.2

revalidate does not work as expected

My "quick/dirty" solution was to do this, unfortunately

redirect("/users?r=" + Math.random().toString()); 
jwalcher commented 1 year ago

makes sense, but unstable in the long run

BenocxX commented 1 year ago

I'm new to NextJS. Is it normal that when I naviguate using the <Link> component it runs the matching page component once, but not when I reuse the <Link> a second time.

Basically, I have /home& /dashboard. In my Dashboard component, there is a simple console.log('dashboard'). When I naviguate to /dashboard, it logs "dashboard". But when I go back to /home then again to /dashboard, nothing gets logged out.

I think NextJS is caching the page. I tried setting export const revalidate = 0; but it did not change anything.

Here's my app/dashboard/page.tsx:

import Link from 'next/link';

export const revalidate = 0;

export default async function Dashboard() {
  console.log('dashboard');
  return <div>
    <h1>Dashboard</h1>
    <Link href="/">Home</Link>
  </div>
}

Is this expected behaviour? It seems weird. I've been using SvelteKit before and everytime I naviguated to a page, it mounted the component. Is there something I'm doing wrong? Is this related to this issue?

Thanks for your help!

wonu commented 1 year ago

@BenocxX I also posted a similar question(discuss#49234) before but no one answered :(

jwalcher commented 1 year ago

@BenocxX This has nothing to do with the issue really, but indeed whether pages are rendered statically or dynamically. Setting const revalidate = 0 might not force dynamic rendering if you are not fetching any data, though I'm not sure and have had trouble matching the docs with actual behavior before. Of course, behavior also depends on whether you are running dev mode or production. @wonu The question how many times certain operations are executed has also befuddled me for a long time. It doesn't appear to be a general priority or big impediment however.

BenocxX commented 1 year ago

Thanks @jwalcher! So if I understand correctly, revalidation will only occur if I am fetching data. I didn't think the compiler or bundler would act differently whether I'm fetching or not. Thanks for the advice!

Edit: I tried adding an API call using fetch to my ASP.Net backend API and it did not fix the problem. My component is still rendered only once even with export const revalidate = 0; and next: { revalidate: 0 } in the fetch options.

I feel a bit let down by Next... This should not be hard to do nor as complexe as it feels right now. I hope I'm doing something wrong because this is a bit disappointing.

Second edit: When I check the Network tab in the inspector. I see that I've fetch the /dashboard route once and cache-control is set to no-store, must-revalidate. But when I go back on /home than back again on /dashboard nothing happens. There is no call done even though it said the cache needs to be revalidated.

Third edit: I created a brand new project with nothing related to my previous app. It is as barebones as possible. Default config for everything. I'm also fetching a public json api, not my ASP.Net backend. It still does not work. Same problem as before.

Next version: 13.4.3

Final edit: This issue is my current problem #42991

danielschmitz commented 1 year ago

Third edit: I created a brand new project with nothing related to my previous app. It is as barebones as possible. Default config for everything. I'm also fetching a public json api, not my ASP.Net backend. It still does not work. Same problem as before.

Next version: 13.4.3

have this issue too

can u share this clean project in the github...? tks

jwalcher commented 1 year ago

yes, pls share the clean code

EllisonFord commented 1 year ago

Could anybody please share a full example of revalidation working for them and what version of NextJS they are using?

jwalcher commented 1 year ago

I have a production running 13.3.0 in which res.revalidate is working as per the docs in production mode, and also in dev. app/page.tsx

export default async function Home() {
  const res = await fetch("https://www.timeapi.io/api/Time/current/zone?timeZone=Europe/Amsterdam");
  const data = (await res.json()) as { hour: number; minute: number; seconds: number };
  return (<><h1>Time in Amsterdam is {data.hour}:{data.minute}:{data.seconds}</h1></>);
}

api/revalidate.ts

import fs from "fs";
import path from "path";
import type { NextApiRequest, NextApiResponse } from "next";
type Data = | { message: string; } | { revalidated: Boolean } | { cacheCleared: Boolean } | string;
const directory = process.cwd() + "/.next/cache/fetch-cache";

export default async function handler(req: NextApiRequest, res: NextApiResponse<Data>) {
    if (process.env.NODE_ENV === "development") { console.log("clearing cache");
      fs.readdir(directory, (err, files) => { if (err) throw err;
        for (const file of files) {
          fs.unlink(path.join(directory, file), (err) => { if (err) throw err; });
        }});
      return res.json({ cacheCleared: true });
    } else {
      console.log("revalidating");
      await res.revalidate("/");
      return res.json({ revalidated: true });
    }}

update I just checked in also works in latest 13.4.4-canary.5

EllisonFord commented 1 year ago

ty for the sample @jwalcher , though I should have been more specific.

Anybody have a sample of working code that's using Data Fetching without fetch() revalidation to share? As well as the NextJS version used

jwalcher commented 1 year ago

well, in actuality I am fetching from a graphql endpoint of a local headless CMS (Strapi). It's harder to share as a working code but it's really a completely standard fetch routine in app/page.tsx, and no changes to the above pages/api/revalidate.ts, something like

import { ApolloClient, InMemoryCache, gql } from "@apollo/client";
import moment from "moment";

const client = new ApolloClient({
 uri: "http://localhost:1337/graphql",
 cache: new InMemoryCache(),
 ssrMode: typeof window === "undefined",
  name: "react-web-client",
  connectToDevTools: true,
  defaultOptions: { 
     watchQuery: { fetchPolicy: "no-cache", errorPolicy: "ignore"},
     query: { fetchPolicy: "no-cache", errorPolicy: "all" }
}});

async function getData() {
  const { data, error } = await client.query({
     query: gql`{
         front {
           data {
             id
             attributes {
               updatedAt
    }}}}`,});
   return data;}

export default async function Home() {
   const strapidata = await getData();
  return (<h1>
        Strapi updatedAt:&nbsp;
        {moment(strapidata.front.data.attributes.updatedAt as string).format("HH:mm:ss")}
      </h1> )
}

The cache options in the Apollo client were part of my debugging (in v13.2.x), but I don't think they are essential (anymore). I have not tested again, but it worked in 13.3.0, and should also in latest canary.

Gyurmatag commented 1 year ago

@EllisonFord did you find the example you were looking for here?

MotionForward commented 1 year ago

revalidation doesn't work with pages routing and revalidate: 3600 option in "next": "^13.3.1",

export async function getStaticProps() {
  ...
    return {
      props: {
        scoreboard: scoreboard || null,
        schedule: schedule || null,
        mainVideoOptions,
        teamsStats: teamsStats?.data?.items || null,
        playersStats: playersStats?.data?.items || null,
        highlightsSlider,
        metaDesc: process.env.NEXT_PUBLIC_CURRENT_SEASON,
      },
      revalidate: 3600,
    };
}
nickmarca commented 1 year ago

Same issue here, revalidate doesn't seem to work. Need to refresh the page to actually get an updated version of the server component. Next version: 13.4.3.

This has nothing to do with the fetch cache mechanism I believe. This is Next caching the server components and ignoring the exported const "revalidate" value.

I think I misunderstood how the cache thing works. Not sure why people are disliking this comment, but it actually solved the problem for me: https://github.com/vercel/next.js/issues/49571#issuecomment-1546974737

If I'm not wrong the "revalidate" const only works for incoming requests (which only happens if you refresh the page or navigate via browser address bar). it doesn't change anything on the frontend side of things, like "userRouter" caching.

If what I said above is correct, I'd suggest to improve the docs though. Cause at the moment, the caching part is not very clear about what's being cached. It should be distinguishing clearly the different layers of caching that it might have.

txzira commented 1 year ago

I'm not sure where I found the work around but when I use request.url in my route handler my revalidation works in production (next build && next start) for example:

import prisma from "lib/prisma";
import { NextResponse, NextRequest } from "next/server";

export async function GET(request: NextRequest) {
  if (request.method === "GET") {
    console.log(request.url);

    const categories = await prisma.category.findMany({});
    return NextResponse.json({ categories }, { status: 200 });
  } else {
    return NextResponse.json({ message: "Route no valid", status: 500 });
  }
}

I have no idea why it works but when I remove console.log(request.url) from this route, after adding a category the category list fetched is stale.

Everything works as expected in development mode (next dev) without console.log(request.url) though.

EllisonFord commented 1 year ago

@EllisonFord did you find the example you were looking for here?

Did not get an example that resembles the one in the documentation here. Would be good to have a working example that includes: NextJS version used, Component code, code that's using the component.

jwalcher commented 1 year ago

throws an Error 500 when called on a path that's rewritten by middleware

This isn't supported, see related docs here that mention middleware is not invoked for On-Demand Revalidate requests. This didn't change recently.

@ijjk please could someone take another look at this, perhaps through the issue https://github.com/vercel/next.js/issues/50464

It might be true that middleware is not invoked per se during revalidation. But the headers sent to the Next server during revalidation still depend on whether middleware is active or not. If middleware is active, they look like:

{
  'x-prerender-revalidate': 'ba3b3cf238a4b810b738e4cd2e97ec4a',
  'x-invoke-path': '',
  'x-invoke-query': '',
  'x-middleware-invoke': '1'
} 

while if it is not, we have

{
  'cache-control': '',
  'x-prerender-revalidate': 'ec3fa3db8021c13e6ffb3b45dab92f78',
  'x-middleware-invoke': '',
  'x-invoke-path': '/time',
  'x-invoke-query': '%7B%7D'
}

While x-middleware-invoke appears to be ignored, the subsequent behavior depends on whether the revalidation path is in the pages or app directory. If it is in the pages directory, revalidation works fine (independent of middleware-invoke flag). If the revalidation path is in the app directory, we get an Error 500 if middleware-invoke == 1.

So it might well be a router issue, at which point I'm a bit out of my depth. Thanks!