[🐞] Cookie not set when using `$server`
Describe the bug
Cookie is not set when using server$ with useTask$
Note: same example with useVisibleTask instead useTask works fine
Reproduction
https://github.com/ziimakc/qwik-cookie-not-set
const noCookies = server$(async function (
this: RequestEventBase<QwikCityPlatform>,
): Promise<{}> {
this.cookie.set("x", "x", {
path: "/",
});
return {};
});
export default component$(() => {
useTask$(async () => {
const res = await noCookies(); // cookies not set in browser
});
return ( <h1>Hi 👋</h1> );
});
Steps to reproduce
- Run npm dev
- Open browser
- Check cookies
the problem is with setting headers and streaming. we need to set the headers right away which is why this is fine with useVisibleTask$ since the response and headers are set at the same time
ok I think I got a fix or at least something that will mostly work. so long as the cookie is set before the first write to stream
so the correct solution are plugin@<name>.ts https://qwik.dev/docs/advanced/plugins/
please use plugin@<name>.ts to set request cookies. if you have examples and a repro repo oh what you're trying to do then I can show you how to do it with plugin@<name>.ts/middleware
@PatrickJS and how would I set cookie in plugin that executes before $server, how do I know which server method was called?
If $server this.cookie.set is not supported, then should't it be removed to avoid confusion?
Example repo is provided and I wan't to do simple thing - set cookie when this method is called.
here is a summary of my response on discord
Summary of Why server$ Can't Set Cookies in Certain Cases:
-
Issue with Initial Load:
- When using
server$to set cookies during the initial server-side render, it doesn't work because the response is streamed immediately. - HTTP requires headers (including cookies) to be set before the response is sent, but
server$during SSR streams the response right away, preventing cookies from being set properly.
- When using
-
Client-Side Requests:
- When
server$is called from the client, it works correctly because the response isn't streamed yet, allowing cookies to be set before sending the response.
- When
-
Use of Plugins:
- To handle cookies (especially for authentication) on initial load, it's recommended to use Qwik plugins with an
onRequesthandler. - The plugin middleware can intercept requests and set cookies before the response is sent, ensuring proper handling.
- To handle cookies (especially for authentication) on initial load, it's recommended to use Qwik plugins with an
-
Alternative Approach:
- If you need to set cookies after the initial load, consider using
useVisibleTask$instead ofuseTask$to ensure theserver$function runs only on client requests, thus allowing cookies to be set correctly. (avoiding initial render issue)
- If you need to set cookies after the initial load, consider using
-
Specific Use Case:
- For scenarios like updating a checkout session each time the cart content changes, a combination of plugin middleware (for initial load) and
server$(for client-side updates) may be needed.
- For scenarios like updating a checkout session each time the cart content changes, a combination of plugin middleware (for initial load) and
-
Documentation:
- The documentation for Qwik plugins and handling such cases needs improvement, which is acknowledged in the conversation.
@PatrickJS from my point of view $server looks the same as $routeLoader and I can see it probably will be added also to $useResource https://github.com/QwikDev/qwik-evolution/issues/65
In all of this cases we have requestEvent of the same type, but only for $server and probably $useResource it will not work on some specific cases when setting cookie on initial render.
Changing types or creating eslint rules seems not to be possible as it depends on where this method is used, so I agree that it seems just documenting this for now is the best option.
Would this same thing happen if using action.submit?
I believe this was solved when we added AsyncLocalStorage to the requests?
Hello @ziimakc. Please provide the missing information requested above.
Issues marked with STATUS-2: missing info will be automatically closed if they have no activity within 14 days.
Thanks 🙏