Bypass the dynamic `cookies`, `headers` etc... behaviour.
Answered
Jesse posted this in #help-forum
JesseOP
How do you bypass the dynamic
I have some example code.
And when I make a
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.
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.
21 Replies
@Jesse How do you bypass the dynamic `cookies`, `headers` etc... behaviour.
I have some example code.
ts
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.
there is no guarantee the header is there so you need to handle the case where the header is not there
@joulev there is no guarantee the header is there so you need to handle the case where the header is not there
JesseOP
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.
@Jesse 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.
Why is there always a header? Where do you add the header? The middleware?
@joulev Why is there always a header? Where do you add the header? The middleware?
JesseOP
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;
}
@Jesse 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
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.
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
JesseOP
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 headerthat's why in all cases if we're even getting to the point of the page rendering there should be a user header
@Jesse It's not a server action it's just a helper function meant to be run on the server
I meant the set function in your server action, it will rerun the server component. So you have to not do that.
@Jesse 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
Yes, and I’m telling you to move auth checks to server-side functions run in server components instead.
@joulev I meant the set function in your server action, it *will* rerun the server component. So you have to not do that.
JesseOP
oh you mean the
cookies().set()
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