Next.js Discord

Discord Forum

Bypass the dynamic `cookies`, `headers` etc... behaviour.

Answered
Jesse677 posted this in #help-forum
Open in Discord
Avatar
How do you bypass the dynamic cookies, headers etc... behaviour.
I have some example code.
export async function getUserFromHeaders() {
  return JSON.parse((await headers()).get("user") as string) as UserType;
}


And when I make a set request to cookies in a server action being called in one of my client components, it causes a re render of the server component using the above code and breaks it as the header is no longer present.
Answered by joulev
Bad choice. Put getUser() in a React.cache() and call it anywhere you need to get the user. Run redirect() in there if the user doesn’t exist. Instead of depending on middleware.

Server actions will rerun the server component if it has any revalidation in it. If you absolutely want to follow through this auth method, use something other than server actions.
View full answer

21 Replies

Avatar
there is no guarantee the header is there so you need to handle the case where the header is not there
Avatar
I can handle the case where there's no header but there should always be a header unless some unexpcted render is happening which is the case here. I can fix it by just having a try catch but I want to actually fix what's causing the problem.
Avatar
Why is there always a header? Where do you add the header? The middleware?
Avatar
Yes and if the header isn't present it's handled in the middleware by redirecting it to a different page where the header isn't required and hence not generated
As for now this is my alternative.
let user: UserType | undefined;

export async function getUserFromHeaders() {
  const fetchedUser = JSON.parse((await headers()).get("user") as string);
  if (fetchedUser) user = fetchedUser;
  return user as UserType;
}
Avatar
Bad choice. Put getUser() in a React.cache() and call it anywhere you need to get the user. Run redirect() in there if the user doesn’t exist. Instead of depending on middleware.

Server actions will rerun the server component if it has any revalidation in it. If you absolutely want to follow through this auth method, use something other than server actions.
Answer
Avatar
It's not a server action it's just a helper function meant to be run on the server
there's no "use server" involved
the reason it's dependent on middleware is because that's where auth takes place, it checks cookie, checks db and as a result gets the user and passes it as a header all pages using user from headers would be protected pages and as a result would need to be signed in for and if they're signed in they'll have a user passed as a header
that's why in all cases if we're even getting to the point of the page rendering there should be a user header
Avatar
I meant the set function in your server action, it will rerun the server component. So you have to not do that.
Yes, and I’m telling you to move auth checks to server-side functions run in server components instead.
Avatar
oh you mean the cookies().set()
ok that's fair enough
would it be viable to do this in a layout.tsx which passes it to context
Since beforehand I had this.
import Footer from "@/components/Footer";
import UserStateProvider from "@/components/UserStateProvider";
import ProtectedNavbar from "@/components/navbar/ProtectedNavbar";
import PurchasePlan from "@/components/protected/PurchasePlan";
import SuccessMessageHandler from "@/components/protected/SuccessMessageHandler";
import VerifyEmail from "@/components/protected/VerifyEmail";
import { getUserFromHeaders } from "@/lib/server-utils";
import { LayoutPropsType } from "@/lib/types";

export default async function Layout({ children }: LayoutPropsType) {
  const user = await getUserFromHeaders();

  return (
    <UserStateProvider user={user}>
      <ProtectedNavbar />
      <SuccessMessageHandler />
      {!user.verified ? (
        <VerifyEmail />
      ) : user.plan === "Pending" ? (
        <PurchasePlan />
      ) : (
        children
      )}
      <Footer />
    </UserStateProvider>
  );
}
Could I just move auth into there
if so, is there a way I can cache the fetching of the user for the page being rendered, since I don't want it to run in layout.tsx and then again in page.tsx
export default async function Layout({ children }: LayoutPropsType) {
  const access_token = (await cookies()).get("access_token")?.value
  const {signedIn, user} = await isSignedIn(access_token);

  if(!signedIn) redirect("/signin")

  return (
    <UserStateProvider user={user}>
      <ProtectedNavbar />
      <SuccessMessageHandler />
      {!user.verified ? (
        <VerifyEmail />
      ) : user.plan === "Pending" ? (
        <PurchasePlan />
      ) : (
        children
      )}
      <Footer />
    </UserStateProvider>
  );
}
would the signedIn function result be cached or will I have to implement it myself
Avatar
React.cache as I said above