Redirect troubles

It's the classic use-case where I want a page to redirect if a user isn't logged in.
// lib/server.ts
export const getCurrentUser = cache(async (): Promise<User> => {
const event = await getRequestEvent();
if (!event || !event.locals.session || !event.locals.user) {
console.log("should redirect");
throw redirect("/login"); // also tried with return
}
return event.locals.user;
}, "curruser");
// lib/server.ts
export const getCurrentUser = cache(async (): Promise<User> => {
const event = await getRequestEvent();
if (!event || !event.locals.session || !event.locals.user) {
console.log("should redirect");
throw redirect("/login"); // also tried with return
}
return event.locals.user;
}, "curruser");
export default function Page() {
const user = createAsync(async () => { // also tried with createResource()
return await getCurrentUser();
});
export default function Page() {
const user = createAsync(async () => { // also tried with createResource()
return await getCurrentUser();
});
For some reason this doesn't work at all. The page will continue loading and error out because of trying to access properties of undefined. It used to work a long while ago when I created this, and I have no idea when it broke. As far as I can tell, this should work according to docs too. So why is there no redirection happening?
7 Replies
peerreynders
peerreynders7d ago
The page will continue loading and error out because of trying to access properties of undefined.
The page will have to be prepared to deal with undefined regardless, as user() will return undefined while the getCurrentUser() promise has not yet settled … and that part of the page isn't shown.
peerreynders
peerreynders7d ago
Conceptually the redirect doesn't actually "happen" when the cache runs. It simply means that the cache point resolves to a redirect response value that when accessed via a createAsync signal should compel the page to follow the redirect. So the page will still initially load but once user() goes from undefined to the resolved/rejected redirect response value the page should follow the redirect. This is why it is important to warm the cache point in the route load to minimize the time the loading page will have the signal sitting at undefined.
peerreynders
StackBlitz
Quick fetch example - StackBlitz
A Solid TypeScript project based on @solidjs/meta, @solidjs/router, solid-js, typescript, vite and vite-plugin-solid
Xzayler
Xzayler7d ago
Well so far I think the reason I didn't have the undefined issue is that everything was in a Suspense component and I ran the getCurrentUser function in a createResource(). It definitely worked before but an old update may have broken it without me realising. Could have happened anytime between now and 3 months ago I think. I now included a <Show when={user()} fallback={<div>Loading...</div>}> component to avoid getting the undefined properties error, but it hangs forever on the fallback. Basically the getCurrentUser function does seem to throw but then nothing happens...
peerreynders
peerreynders6d ago
This example https://stackblitz.com/edit/stackblitz-starters-7datdm?file=src%2Froutes%2Fquotes.tsx redirects as expected with
const quotes = cache(async () => {
throw redirect('/');

const response = await fetch('/quotes.json');
if (!response.ok)
throw new Error(`${response.status}: ${response.statusText}`);

const wrapper = (await response.json()) as QuotesWrapper;
return wrapper.quotes;
}, 'quote-data');
const quotes = cache(async () => {
throw redirect('/');

const response = await fetch('/quotes.json');
if (!response.ok)
throw new Error(`${response.status}: ${response.statusText}`);

const wrapper = (await response.json()) as QuotesWrapper;
return wrapper.quotes;
}, 'quote-data');
with solid-router 0.13.3 through 0.13.6
peerreynders
StackBlitz
Quick fetch example - StackBlitz
A Solid TypeScript project based on @solidjs/meta, @solidjs/router, solid-js, typescript, vite and vite-plugin-solid
Xzayler
Xzayler5d ago
Ok I managed to get closer to figuring out the issue. Basically if I have the function that throws the redirect in a file that has a "use server"; line at the top, it won't work. I could not recreate this in stackblitz, weirdly enough, but for me this works:
// lib/gcu.ts
export const getCurrentUser = cache(async (): Promise<User> => {
"use server";
...
throw redirect("/login");
...
}
// lib/gcu.ts
export const getCurrentUser = cache(async (): Promise<User> => {
"use server";
...
throw redirect("/login");
...
}
But this doesn't:
// lib/gcu.ts
"use server";
export const getCurrentUser = cache(async (): Promise<User> => {
...
throw redirect("/login");
...
}
// lib/gcu.ts
"use server";
export const getCurrentUser = cache(async (): Promise<User> => {
...
throw redirect("/login");
...
}
Is there a reason for this? Now everything seems to work fine by just putting the function in a separate file without a file-wide "use server; and sticking it in the function instaed.
peerreynders
peerreynders5d ago
Instinctually in such a case I've always done this (and it works):
// server/gcu.ts
"use server";
async function getUser() {
//…
throw redirect("/login");
//…
}

export {
getUser
};
// server/gcu.ts
"use server";
async function getUser() {
//…
throw redirect("/login");
//…
}

export {
getUser
};
// lib/gcu.ts
import { getUser } from '../server/gcu';

const getCurrentUser = cache(getUser, 'current-user');

export {
getCurrentUser
}
// lib/gcu.ts
import { getUser } from '../server/gcu';

const getCurrentUser = cache(getUser, 'current-user');

export {
getCurrentUser
}
cache() is a client side feature and has no business being inside a server section.
GitHub
solid-start-notes-basic/src/api.ts at f13872d001dc1680f84330337ac61...
Basic client rendered notes app using SolidStart beta - peerreynders/solid-start-notes-basic
Xzayler
Xzayler5d ago
Oh I see. Thanks for the help and clearing that up for me!