Next.js Discord

Discord Forum

SSG when using Prisma in App Router

Answered
Plott Hound posted this in #help-forum
Open in Discord
Plott HoundOP
In pages I would have done this:
export default function ExercisesPage({ exercises }) {
  return (
    <>
      <ExerciseTable exercises={exercises} />
    </>
  );
}

export async function getStaticProps() {
  const exercises = await getExercises();
  
  return {
    props: {
      exercises
    },
    revalidate: 86400
  };
}

This would generate a static page. In app router it seems the same usage would be:
async function fetchExercises() {
  const exercises = await getExercises();
  return exercises;
}

export default async function ExercisesPage() {
  const exercises = await fetchExercises();

  return (
    <>
      <ExerciseTable exercises={exercises} />
    </>
  );
}

this will be rendered as SSR which i don't want since the data is static and will rarely change.

The closest thing i can find to a solution is to use unstable cache with SSR:
'use server';
import { unstable_cache } from 'next/cache';
import prisma from '@/db/prisma';

async function fetchExercisesFromDB() {
    console.log("Fetching exercises from database...");
    return await prisma.exercise.findMany({
        take: 100,
        select: {
            id: true,
            name: true,
        },
    });
}

export default async function getExercises() {
    const getCachedOrFetchExercises = unstable_cache(
        fetchExercisesFromDB,
        'exerciseList',
        { tags: ['exercises'], revalidate: 6000 }
    );

    return await getCachedOrFetchExercises();
}

The console log proves that the data is only fetched when the cache is revalidated but still the main page will be SSR. Does anyone have any suggestions? I really don't want to fetch from my own route handler to leverage fetch and force-cache.
Answered by aardani
coz getServerSession itself uses headers() and cookies() which automatically opts any route to Dynamic SSR
View full answer

23 Replies

Plott HoundOP
I realised why this is happening:
When I setup NextAuth I made my root layout use an async default export:
export default async function RootLayout({
This is causing everything to be SSR λ /

Is there anyway I can still force routes to be SSG? Or would I need to make special non-async layouts for those routes?
coz getServerSession itself uses headers() and cookies() which automatically opts any route to Dynamic SSR
Answer
The closest thing i can find to a solution is to use unstable cache with SSR:
Are you trying to create Server Action here? because you used use server
The console log proves that the data is only fetched when the cache is revalidated but still the main page will be SSR. Does anyone have any suggestions? I really don't want to fetch from my own route handler to leverage fetch and force-cache.

What do you actually want? an SSG page? or is SSR okay?
coz spoiler alert, yes, Data Cache is only achievable using fetch() and unstable_cache()
@aardani `export const dynamic = 'force-static'` but i doubt NextAuth works in SSG
Plott HoundOP
this worked thanks.
@aardani coz spoiler alert, yes, Data Cache is only achievable using fetch() and unstable_cache()
Plott HoundOP
With Next Auth I wanted to make the session available across all routes so even if i didnt need to access the session I could protect the routes with !session. this worked but then I realised I could do the same but better with middleware. After setting up middleware and removing my server component method of protecting routes I was confused why pages that didnt use the session were stil SSR. Today I realised back at the start of the project when i was following a tutorial i made the root layout async which explains the behaviour.

I guess after coming to that point i wasnt sure if it was best to make a new layout that wasnt async for some pages or see if there was a way to manually make the pages static. I'm going to test the performance of both export const dynamic = 'force-static' and using a second layout.tsx without async and see which works best.

Thanks for your help! I'm still learning
I'm guessing now the page is rendered as static (as i wanted) i dont need to cache the db fetch anymore
I was confused why pages that didnt use the session were stil SSR.
this needed to be checked at the page and every layout segment to be sure you dont use any dynamic feature. easiest way to check is using dynamic = 'error', easiest way to override is to using 'force-static'
root layout async which explains the behaviour.
hmmm root layout being async shouldnt affect that but im not fully sure
async or no async should not affect staticity of a route
Plott HoundOP
i'm gonna do some more testing now before marking this as answered if thats ok. just in case anyone else finds this useful.
@aardani async or no async should not affect staticity of a route
Plott HoundOP
Thats what i was thinking. maybe i have something in the sidebar using async or something, i'll do some more investigating and report bakc. once again thank you so much for taking the time to help, really appreciated!
Plott HoundOP
@aardani update:

import {Providers} from "./providers";
import './globals.css';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {

  return (
    <html lang="en">
      <body>
          <Providers>
            {children}
          </Providers>
      </body>
    </html>
  )
}

static pages are rendered correctly with this code. but with my original code:

import {Providers} from "./providers";
import { getServerSession } from "next-auth";
import SessionProvider from "@/components/SessionProvider";
import './globals.css';
export default async function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = await getServerSession();

  return (
    <html lang="en">
      <body>
        <SessionProvider session={session}>
          <Providers>
            {children}
          </Providers>
        </SessionProvider>
      </body>
    </html>
  )
}

all routes are rendered as SSR at build time.
@/components/SessionProvider is just me exporting SessionProvider with use client:
"use client";
import { SessionProvider } from "next-auth/react";
export default SessionProvider;


is this expected behaviour?
Plott HoundOP
Thanks, I think i got it now
Plott HoundOP
Thanks for all your help I’m marking it as resolved.