Next.js Discord

Discord Forum

Get layout to run on every request

Unanswered
Sphecid wasp posted this in #help-forum
Open in Discord
Sphecid waspOP
Hi, I've got the following layout in app/(protected)/layout.tsx

import { redirect } from "next/navigation";
import { getCurrentSession } from "@/lib/auth/session.cached";
import { ROUTES } from "@/modules";
import { AppShell } from "@/modules/app-shell";

export default async function ProtectedLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const { session } = await getCurrentSession();
  if (!session) redirect(ROUTES.LOGIN);

  return <AppShell>{children}</AppShell>;
}


And I'd like the logic to run every time I request a new URL for any request used by this layout. I've tried adding export const dynamic = 'force-dynamic'; but it doesn't seem to work. Is that even how the dynamic route segment config works? I've tried using an uncached version of the function with force-dynamic also but I'm still not having any luck.

25 Replies

Asian black bear
That's by design, layouts do not rerender if you stay at the same route segment because that's wasteful and unnecessary. Auth shouldn't be done in layouts anyways. Only in the middleware, pages or API/action code.
@Asian black bear That's by design, layouts do not rerender if you stay at the same route segment because that's wasteful and unnecessary. Auth shouldn't be done in layouts anyways. Only in the middleware, pages or API/action code.
Sphecid waspOP
Thanks for the suggestion. Why pages instead of layout? Are there any kind of performance or functionality considerations or is it strictly convention?
Mucuchies
Its convention. And just generally more safe. Just dont do it in layout
@Sphecid wasp Thanks for the suggestion. Why pages instead of layout? Are there any kind of performance or functionality considerations or is it strictly convention?
It's for security, layouts only run once so the auth check only happens once for all routes under the layout, hence why it's a waste of resources and time to check auth in layout.

If you want to protect a page -> do it in the page
if you want to protect the data -> do it in the data fetching function
If you want to protect the API/action -> do it in the API/action
Also, is this breaking any "best practices"?

import { getCurrentSession } from "@/lib/auth/session.cached";

export default async function DashboardLayout({
  admin,
  customer,
  employee,
  employee_delegate,
  technician,
}: Readonly<{
  admin: React.ReactNode;
  customer: React.ReactNode;
  employee: React.ReactNode;
  employee_delegate: React.ReactNode;
  technician: React.ReactNode;
}>) {
  const { user } = await getCurrentSession();

  const component = {
    admin,
    customer,
    employee,
    employee_delegate,
    technician,
  };

  return user ? component[user.role] : null;
}
@Sphecid wasp Does this remain true if you set force-dynamic in the layout?
No, that will run the layout on every request which is not recommended at all, that is what middleware if for.
And by checking the role I mean re-calling the session you don’t want to use the same instance as the layout since it won’t change on each render.
I understand needing to protect a route/action but once the layout renders the correct page, there's no risk of rendering the page for another role, right?
Asian black bear
Navigating from one nested route to another needs a new check because you are not guaranteed that the current user has the same permissions as the last time they accessed the previous page.
Asian black bear
On a different note, I am not even certain that using parallel routes for your use case is even good. I can't recall whether or not all parallel routes are being rendered server-side when you perform a request, but I'd wager that is the case. This means a customer accessing the dashboard would invoke the execution of the other routes and possibly waste computational power and throw auth errors when attempting to render the admin route etc.

You should check whether this is the case, as I'm not 100% certain on that.
Sphecid waspOP
It seems like when I go to the dashboard route that it's rendering the correct component and I don't see any errors in the console.
I see "Admin Dashboard" for example when I am logged in as an admin.
Yeah but as near said, if you don’t check in the underlying pages or components if you were to revoke your admin permissions while in the admin dashboard and navigate around it you’ll be able to which should not be allowed.
Sphecid waspOP
An aside, I'm getting this error in my console now when I try to go to / in the app without being logged in:

[Error] WebSocket connection to 'ws://localhost:3000/_next/webpack-hmr' failed: WebSocket is closed due to suspension.


This is the root page:

import { redirect } from "next/navigation";
import { getCurrentSession } from "@/lib/auth/session.cached";
import { ROUTES } from "@/modules";
import { checkInitialUserRoles } from "@/modules/setup";

export default async function Home() {
  const { hasAdmin, hasDelegate } = await checkInitialUserRoles();
  if (!hasAdmin || !hasDelegate) {
    return redirect(ROUTES.SETUP);
  }

  const { session } = await getCurrentSession();
  if (!session) {
    return redirect(ROUTES.LOGIN);
  }

  redirect(ROUTES.DASHBOARD);
}
@Sphecid wasp It seems like when I go to the dashboard route that it's rendering the correct component and I don't see any errors in the console.
Asian black bear
I was talking about rendering all routes on the server, regardless of whether only one is eventually sent to the client.
@Asian black bear I was talking about rendering all routes on the server, regardless of whether only one is eventually sent to the client.
Sphecid waspOP
How can I check to see if they're all being rendered? console.log?
Yeah, just verified, they're all rendering, lol
I mean I guess that's funny because conditional rendering using parallel routes is an example in the docs.
Asian black bear
Yeah, I just saw it myself. It's technically working but the docs don't point out the side-effect of all other parallel routes being invoked and causing unnecessary computations which might yield to misleading warnings and errors.