How to set a cookie when using `createRouteData` or `createServerData$`?

For user authentication it may be necessary to set a cookie. In Remix, for example, it is solved so that a loader can either directly return a value or alternatively a Response. The Response can then be used to set the Set-Cookie header. Remix seems to recognize whether the values are returned directly or via a Response, e.g. with json({...}), and interprets the types correctly when calling useLoaderData<typeof loader>(). This does not seem to work with SolidStart. Is there an alternative way? https://remix.run/docs/en/v1/route/loader
9 Replies
Massukka
Massukka2y ago
https://discord.com/channels/722131463138705510/1055857658952962058 This is how I made it work. Dunno if it's the best way though. Server side I just use the createCookieSessionStorage
binajmen
binajmen2y ago
That's more or less the same: you should use createCookieSessionStorage in conjonction with createServerData$() that would return a Response, like in Remix. I think it is necessary to do it only in createServerData$ so you don't leak the SESSION_SECRET: the cookie should be signed on the server – to be confirmed
binajmen
binajmen2y ago
SolidStart Beta Docuentation
SolidStart Beta Documentation
Early release documentation and resources for SolidStart Beta
Fabian Hiller
Fabian Hiller2y ago
This way the returned value of useRouteData is a Response and I cannot use the data in the component as it works in Remix. Maybe @nksaraf knows the answer.
nksaraf
nksaraf2y ago
if u return a Response from createServerData, you can use that data, even if its Response. You will just have to get the data using response.json(), might need another resource
Fabian Hiller
Fabian Hiller2y ago
@nksaraf thanks for your answer! Is it meant as in the following code example?
import { createResource } from 'solid-js';
import { json, useRouteData } from 'solid-start';
import { createServerData$ } from 'solid-start/server';
import { getCurrentUser } from '../api';

export function routeData() {
return {
getUser: createServerData$((_, event) => {
const user = getCurrentUser(event.request);
return json(user.data, { headers: { 'Set-Cookie': user.cookie } });
}),
};
}

export default function HomePage() {
const { getUser } = useRouteData<typeof routeData>();
const [data] = createResource(getUser, (user) => user.json());
return <h1>{data.username}</h1>;
}
import { createResource } from 'solid-js';
import { json, useRouteData } from 'solid-start';
import { createServerData$ } from 'solid-start/server';
import { getCurrentUser } from '../api';

export function routeData() {
return {
getUser: createServerData$((_, event) => {
const user = getCurrentUser(event.request);
return json(user.data, { headers: { 'Set-Cookie': user.cookie } });
}),
};
}

export default function HomePage() {
const { getUser } = useRouteData<typeof routeData>();
const [data] = createResource(getUser, (user) => user.json());
return <h1>{data.username}</h1>;
}
Is it possible to implement createRouteData and createServerData similar to Remix, so that it makes no difference whether you return the values directly or via a Response, e.g. with json() ? Otherwise unpacking the data in the component leads to a lot of boilerplate code. Especially with createRouteData this would make the code more consistent and lead to a better DX. @nksaraf what are your thoughts on this? If you're interested, I can take a look at the code on GitHub and create a PR. Cookies are not uncommon in data fetching, so I expect more people will run into this "problem" in the future.
nksaraf
nksaraf2y ago
I completely agree .. we need a good way of doing this Would love to look at PRs
colinshen
colinshen2y ago
GitHub
Expose responseHeaders to createServerData$ by oscartbeaumont · Pul...
This PR exposes a way to set HTTP headers on the response within the createServerData$ function. This makes it possible to do: export function routeData() { return createServerData$((_, { respons...
Fabian Hiller
Fabian Hiller2y ago
Thanks for linking @colinshen!