Next.js Discord

Discord Forum

Help using cookie-mutating code in route handler through fetch call

Answered
declspecl posted this in #help-forum
Open in Discord
Hi all! Currently, I need to use some mutating server-side code (that I defined in a module with "use server") automatically inside of a server component. And by mutating, I mean it mutates cookies. It absolutely can not be through middleware unfortunately, but it would make it a lot simpler.

I realized that I can not just use the server action in the server component since mutating cookies is not allowed in server components. So, I tried creating a route handler instead, but my fetch calls are not sending the cookies that I need in that route handler. This is my fetch code (in a server component):
const res = await fetch("http://localhost:3000/api/session/refresh", {
    method: "POST",
    mode: "same-origin",
    headers: {
        "Content-Type": "application/json"
    },
    credentials: "include"
});
and for testing purposes this is my route handler code right now (in /app/api/session/refresh/route.ts):
export async function POST(request: NextRequest): Promise<NextResponse> {
    console.log(request.cookies.getAll());

    return NextResponse.json(null);
}
and every time I make that fetch, [] is printed to the console. Is there something I am doing wrong? Is there an easier way of doing this? Thank you very much!
Answered by declspecl
Geez I went down a bit of a rabbit hole. I was able to port the fetch calls to middleware due to a rewrite of certain parts to avoid my orm client running on edge middleware (can't), but setting cookies in middleware is apparently a pretty prevalent and not well documented issue.

For others with the same question, this github thread and specifically this comment makes it pretty simple though. Hopefully this works in deployment since apparently dev behavior != deployment behavior with this kind of stuff https://github.com/vercel/next.js/issues/49442#issuecomment-2041387328
View full answer

3 Replies

Update: I was able to forcefully send the cookie by adding the header to the fetch like so:
const res = await fetch("http://localhost:3000/api/session/refresh", {
    method: "POST",
    mode: "same-origin",
    headers: {
        "Content-Type": "application/json",
        "Cookie": `session=${mySessionCookie};`
    }
});
but now I realized that I need the session cookie's expiration to be updated in the server component still, but I can't directly update it since I can't mutate cookies in the server component. Any ideas on how to get around this?
Geez I went down a bit of a rabbit hole. I was able to port the fetch calls to middleware due to a rewrite of certain parts to avoid my orm client running on edge middleware (can't), but setting cookies in middleware is apparently a pretty prevalent and not well documented issue.

For others with the same question, this github thread and specifically this comment makes it pretty simple though. Hopefully this works in deployment since apparently dev behavior != deployment behavior with this kind of stuff https://github.com/vercel/next.js/issues/49442#issuecomment-2041387328
Answer
@declspecl Hi all! Currently, I need to use some mutating server-side code (that I defined in a module with `"use server"`) automatically inside of a server component. And by mutating, I mean it mutates cookies. It absolutely can not be through middleware unfortunately, but it would make it a lot simpler. I realized that I can not just use the server action in the server component since mutating cookies is not allowed in server components. So, I tried creating a route handler instead, but my fetch calls are not sending the cookies that I need in that route handler. This is my fetch code (in a server component):ts const res = await fetch("http://localhost:3000/api/session/refresh", { method: "POST", mode: "same-origin", headers: { "Content-Type": "application/json" }, credentials: "include" }); and for testing purposes this is my route handler code right now (in /app/api/session/refresh/route.ts):ts export async function POST(request: NextRequest): Promise<NextResponse> { console.log(request.cookies.getAll()); return NextResponse.json(null); }and every time I make that fetch, `[]` is printed to the console. Is there something I am doing wrong? Is there an easier way of doing this? Thank you very much!
if a server side function invoked on server, which isn't a server action.
A server action is a server side function which get invoked on client side with form, useEffect or event handler which will make a POST request to the current route.

Using route handler inside the page component doesn't set the cookies also. Currently, we can't set the cookie in the page response.
either handle it in middleware or make the request on client side