Closed wkd-kapsule closed 1 year ago
for me, setting cookies is working as expected, but when calling getting cookies from a route handler, there are no cookies (null)
ResponseCookies {
_parsed: Map(0) {},
_headers: HeadersList {
cookies: null,
[Symbol(headers map)]: Map(0) {},
[Symbol(headers map sorted)]: []
}
}
I'm having the same issue.
Under /app/page.tsx, I have
export async function getConfig() {
const configRes = await fetch('http://localhost:4200/api/config', {
credentials: 'include', // cross-origin cookies
});
return await configRes.json();
}
export default async function Index() {
const config = await getConfig();
return <div>Index Page {JSON.stringify(config)}</div>;
}
Under /app/api/config/route.ts
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
export async function GET(req: Request) {
// @ts-ignore
cookies().set({
name: 'cookieName',
value: 'true',
httpOnly: true,
});
return new NextResponse(
JSON.stringify({
t: 'test',
}),
{ status: 200 },
);
// const response = NextResponse.json(
// { data: 'config' },
// {
// status: 200,
// },
// );
// response.cookies.set('login', 'true', {
// path: '/',
// httpOnly: false,
// secure: true,
// maxAge: 60 * 60 * 24 * 7,
// sameSite: 'none',
// });
// return response;
}
I tried both commented and uncommented in route.ts, but none of this is working Moreover if I try to set cookie via server actions it works correctly.
for me, setting cookies is working as expected, but when calling getting cookies from a route handler, there are no cookies (null)
ResponseCookies { _parsed: Map(0) {}, _headers: HeadersList { cookies: null, [Symbol(headers map)]: Map(0) {}, [Symbol(headers map sorted)]: [] } }
Would you please provide the code you used to set the cookie?
I'm having the same issue.
Under /app/page.tsx, I have
export async function getConfig() { const configRes = await fetch('http://localhost:4200/api/config', { credentials: 'include', // cross-origin cookies }); return await configRes.json(); } export default async function Index() { const config = await getConfig(); return <div>Index Page {JSON.stringify(config)}</div>; }
Under /app/api/config/route.ts
import { cookies } from 'next/headers'; import { NextResponse } from 'next/server'; export async function GET(req: Request) { // @ts-ignore cookies().set({ name: 'bet9ja', value: 'true', httpOnly: true, }); return new NextResponse( JSON.stringify({ t: 'test', }), { status: 200 }, ); // const response = NextResponse.json( // { data: 'config' }, // { // status: 200, // }, // ); // response.cookies.set('login', 'true', { // path: '/', // httpOnly: false, // secure: true, // maxAge: 60 * 60 * 24 * 7, // sameSite: 'none', // }); // return response; }
I tried both commented and uncommented in route.ts, but none of this is working Moreover if I try to set cookie via server actions it works correctly.
I'm right there with you... I tried both and even setting a Set-Cookie header, none worked. Btw, I think that if you update Typescript to the latest version you won't need these @ts-ignore anymore.
I noticed that the fetch response has set-cookie inside the headers
Response {
[Symbol(realm)]: { settingsObject: {} },
[Symbol(state)]: {
aborted: false,
rangeRequested: false,
timingAllowPassed: false,
requestIncludesCredentials: false,
type: 'default',
status: 200,
timingInfo: null,
cacheState: '',
statusText: '',
headersList: HeadersList {
[Symbol(headers map)]: [Map],
[Symbol(headers map sorted)]: null
},
urlList: [],
body: { stream: undefined, source: [Uint8Array], length: 12 }
},
[Symbol(headers)]: HeadersList {
[Symbol(headers map)]: Map(7) {
'connection' => 'close',
'content-encoding' => 'gzip',
'content-type' => 'text/plain;charset=UTF-8',
'date' => 'Tue, 18 Jul 2023 07:32:30 GMT',
'set-cookie' => 'cookieName=true; Path=/; HttpOnly',
'transfer-encoding' => 'chunked',
'vary' => 'RSC, Next-Router-State-Tree, Next-Router-Prefetch, Accept-Encoding'
},
[Symbol(headers map sorted)]: null
}
}
Yes, the set-cookie header is shown when I log the headers and yet, no cookie...
I made some further tests.
I created another api route under pages folder pages/api/testapi.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { serialize } from 'cookie';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const cookie = serialize('hello-cookie', 'api-hello-cookie-value', {
path: '/',
});
res.setHeader('Access-Control-Expose-Headers', '*');
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
res.setHeader('Access-Control-Allow-Credentials', 'true');
// res.setHeader('Set-Cookie', `token1=token1`);
res.setHeader('Set-Cookie', cookie);
res.status(200).json({ name: 'John Doe' });
}
I noticed that pointing the browser to http://localhost:4200/api/testapi, the cookie is correctly set and it is available to whole application. But if use that api endpoint within a fetch inside a page
export async function getConfig() {
const configRes = await fetch('http://localhost:4200/api/prova');
console.log(configRes);
return await configRes.json();
}
export default async function Index() {
await getConfig();
return <div>Index Page </div>;
}
it doesn't work
I made an almost complete codesandbox here https://codesandbox.io/p/sandbox/silly-cookies-vhlvvn check it out. I created a server action to show that the cookie is setted correctly while using server actions
Hi everyone, this is the expected behavior, but we should explain it better in the docs.
You cannot set cookies during render (inside Pages, Layouts, etc.). When a fetch request is made (eg. via a Route Handler), the Set-Cookie
header won't be accounted for during the rendering. Route Handlers do support setting cookies which is documented here.
You can verify this by directly visiting an endpoint that uses Route Handlers. (Observation here https://github.com/vercel/next.js/issues/52799#issuecomment-1639831773)
When using Server Actions, the recommendation is to import the logic of the Route Handler instead of doing a fetch call, since you can set cookies via cookies().set
inside a Server Action. See: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#using-headers
Hi everyone, this is the expected behavior, but we should explain it better in the docs.
You cannot set cookies during render (inside Pages, Layouts, etc.). When a fetch request is made (eg. via a Route Handler), the
Set-Cookie
header won't be accounted for during the rendering. Route Handlers do support setting cookies which is documented here.You can verify this by directly visiting an endpoint that uses Route Handlers. (Observation here #52799 (comment))
When using Server Actions, the recommendation is to import the logic of the Route Handler instead of doing a fetch call, since you can set cookies via
cookies().set
inside a Server Action. See: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#using-headers
Hi, thanks for the answer. Yet, it is still confusing to me.
The link you've provided to set a cookie in a Route Handler is using server action, which I'm not because it is still experimental. Other than that, how am I supposed to access/trigger that cookie function in the Route Handler if I can't fetch the Route? You talk about "directly visiting an endpoint". Am I supposed to redirect my users to a blank /api page just for the cookie to be set? This looks like a wobbly, barely effective workaround.
But I guess it doesn't even matter. Because my use case is involving a fetch request. During that fetch, depending on the data obtained, I want to set a corresponding cookie. Given what you said, Nextjs is intentionally built to make it impossible...
"You can set cookies using Route Handlers" "The way to access a Route Handler is a fetch request" "You can't set a cookie using a fetch request and it is an intended behavior"
Isn't it a bit conflicting or am I getting something wrong?
Hello @kapsule-studio. Not sure if this is already been suggested regarding the primary issue, but I think the issue is that when rendering your page, the fetch
call you make inside of getConfig
occurs on the server-side, where the browser cannot receive and store the cookies from the headers.
Some solutions you may try:
The suggestion @balazsorban44 is making is to copy over similar logic as you have inside of your route handler into the server action, rather than making a fetch request. According to the docs, the cookies().set
method should appropriately set the cookies to be returned to the browser and stored by the browser once the page is rendered. One thing I found trying this was to trigger the server action via a form submission.
An alternate potential solution to try is to opt out of the server-side rendering into client-side rendering and use something such as a useEffect
to make the fetch request client-side, this way the response object would make its way back to the browser where it can set the cookies appropriately. For this, you may have to use credentials: 'include'
in the fetch request.
Both of these approaches are in this CodeSandbox example. You may have to run them locally to notice cookies being set. I did not see them getting set in the CodeSandbox environment.
I've read the docs and tried many times but still fail, cookies still undefined..
How to get cookies with use client
in page.tsx? The docs about cookies is not complete..
Update:
Finally I've made it, with using route and call it with
fetch('http://localhost:3000/api/session', { cache: 'no-store' })
I keep getting this error
Error: Cookies can only be modified in a Server Action or Route Handler.
Is possible set cookie on server side? My code
'use server'
import { cookies } from 'next/headers'
async function deleteCookie(name: string) {
cookies().delete(name)
}
This function is called in the server component. I can't set cookies on the client side because httpOnly is required for authorization.
Hi everyone, this is the expected behavior, but we should explain it better in the docs.
You cannot set cookies during render (inside Pages, Layouts, etc.). When a fetch request is made (eg. via a Route Handler), the
Set-Cookie
header won't be accounted for during the rendering. Route Handlers do support setting cookies which is documented here.You can verify this by directly visiting an endpoint that uses Route Handlers. (Observation here #52799 (comment))
When using Server Actions, the recommendation is to import the logic of the Route Handler instead of doing a fetch call, since you can set cookies via
cookies().set
inside a Server Action. See: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions#using-headers
Ohh, I got it, sending a request from client to server only sent the cookies, but making a api call during the render phase inside of server component won't send the cookies, since the client is the one who has the cookies, but in this case we are dealing with server to server
Is there anyway to make sure that cookies is sent even in the api call made inside server components??
I keep getting this error
Error: Cookies can only be modified in a Server Action or Route Handler.
Is possible set cookie on server side? My code
'use server' import { cookies } from 'next/headers' async function deleteCookie(name: string) { cookies().delete(name) }
This function is called in the server component. I can't set cookies on the client side because httpOnly is required for authorization.
I have same issue in 14.0.1, the doc said we can set the cookie in server action - https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations#setting-cookies
You should be able to set cookies in both Server Actions and Route Handlers. The documentation is correct. If you are seeing an issue, please open a new issue with a reproduction so we can investigate your case. Thanks!
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
What is the improvement or update you wish to see?
The current doc on how to set a cookie in the route handler using the app router is wrong. No matter what I try, I never get to set the cookie. Yet, the same method works in the middleware function.
Is there any context that might help us understand?
Help please
Does the docs page already exist? Please link to it.
https://nextjs.org/docs/app/building-your-application/routing/router-handlers#cookies
DX-1790