Next.js Discord

Discord Forum

Does export const revalidate = work for non fetch based data?

Unanswered
joostschuur posted this in #help-forum
Open in Discord
The docs seem to say 'Alternatively, to revalidate all fetch requests in a route segment, you can use the Segment Config Options.', so it looks like the answer is no.

I've set export const revalidate = 3600 in a layout.tsx (not a client component) that queries data from Drizzle and then does a Tanstack React Query prefetchQuery and passed that query client via a HydrationBoundary to the browser. Before I read that part of the docs, I assumed that after an hour, the next request would trigger that whole chain of events again, but even after a second page request, I don't see updated data.

I've set up an API route that calls revalidatePath('/'), which seems to do the trick, but this of course relies on a cron trigger for that route e.g..

Are there any other options for time based revalidation for data coming from third party libraries where fetch is not used?

I ran into errors using unstable_cache during the build process, and using React's own cache doesn't let you specify an expiration time.

5 Replies

@Ray hi, `revalidate` should work there. are you using route handler with `useQuery` on client side?
Client side, I'm just calling a server action in useQuery in a custom hook:

  const query = useQuery({
    queryKey: ['videos', filterOptions],
    queryFn: () => getLatestVideos(filterOptions as FilterOptions),
    staleTime: Infinity,
  });


But when I visit the page, it's not even showing the loading spinner as a sign that it's making a new request.

Obviously on the client side, it says staleTime: Infinity here, but on a new page refresh/load, that React Query cache is blown away anyway.
Here's my whole layout.tsx:

import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query';
import { GeistSans } from 'geist/font/sans';
import type { Metadata } from 'next';
import { Suspense } from 'react';

import '@/styles/globals.css';

import Providers from '@/components/Providers';
import { HomepageProvider } from '@/components/context';
import Header from '@/components/site/Header';
import { resetState } from '@/lib/filter';

import { getLatestVideos } from '@/db/queries';
import { cn } from '@/lib/utils';

export const metadata: Metadata = {
  title: 'LearnByVideo.dev - Find the best development videos',
  description: 'Find the best development videos to learn from.',
};

export const revalidate = 3600;

const recentVideos = await getLatestVideos();
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60 * 60 * 1000,
      refetchOnWindowFocus: false,
    },
  },
});

await queryClient.prefetchQuery({
  queryKey: ['videos', resetState],
  queryFn: () => recentVideos,
});

const dehydratedState = dehydrate(queryClient);

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang='en' suppressHydrationWarning>
      <Providers>
        <HydrationBoundary state={dehydratedState}>
          <body
            className={cn('min-h-screen bg-violet-50 font-sans antialiased', GeistSans.variable)}
          >
            <Suspense fallback={null}>
              <HomepageProvider>
                <Header />
                <main className='container mx-auto px-4 sm:px-10 py-4'>{children}</main>
              </HomepageProvider>
            </Suspense>
          </body>
        </HydrationBoundary>
      </Providers>
    </html>
  );
}
I assumed revalidating, means also rerunning the code at the root scope of this file (not just in RootLayout), so it makes a fresh getLatestVideos call and prefetches etc...
@joostschuur Here's my whole `layout.tsx`: ts import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query'; import { GeistSans } from 'geist/font/sans'; import type { Metadata } from 'next'; import { Suspense } from 'react'; import '@/styles/globals.css'; import Providers from '@/components/Providers'; import { HomepageProvider } from '@/components/context'; import Header from '@/components/site/Header'; import { resetState } from '@/lib/filter'; import { getLatestVideos } from '@/db/queries'; import { cn } from '@/lib/utils'; export const metadata: Metadata = { title: 'LearnByVideo.dev - Find the best development videos', description: 'Find the best development videos to learn from.', }; export const revalidate = 3600; const recentVideos = await getLatestVideos(); const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 60 * 1000, refetchOnWindowFocus: false, }, }, }); await queryClient.prefetchQuery({ queryKey: ['videos', resetState], queryFn: () => recentVideos, }); const dehydratedState = dehydrate(queryClient); export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang='en' suppressHydrationWarning> <Providers> <HydrationBoundary state={dehydratedState}> <body className={cn('min-h-screen bg-violet-50 font-sans antialiased', GeistSans.variable)} > <Suspense fallback={null}> <HomepageProvider> <Header /> <main className='container mx-auto px-4 sm:px-10 py-4'>{children}</main> </HomepageProvider> </Suspense> </body> </HydrationBoundary> </Providers> </html> ); }
hmm, try move the queryClient inside the RootLayout
export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({
    queryKey: ['videos', resetState],
    queryFn: async () => getLatestVideos(),
  });
  const dehydratedState = dehydrate(queryClient);
  
  return (
    <html lang='en' suppressHydrationWarning>
      <Providers>
        <HydrationBoundary state={dehydratedState}>
          <body
            className={cn('min-h-screen bg-violet-50 font-sans antialiased', GeistSans.variable)}
          >
            <Suspense fallback={null}>
              <HomepageProvider>
                <Header />
                <main className='container mx-auto px-4 sm:px-10 py-4'>{children}</main>
              </HomepageProvider>
            </Suspense>
          </body>
        </HydrationBoundary>
      </Providers>
    </html>
  );
}