S
SolidJS7mo ago
Hussein

how do i redirect on load?

i did this
export const route = {
load: async () => {
"use server";
const cookie = getCookie("chat_session");
if (!cookie) throw redirect("/");
},
};
export const route = {
load: async () => {
"use server";
const cookie = getCookie("chat_session");
if (!cookie) throw redirect("/");
},
};
this is not working
15 Replies
Hussein
HusseinOP7mo ago
it crashes the whole server
Brendonovich
Brendonovich7mo ago
redirect is for use within cache/action, try extracting that async function into a cache and calling it in load
Hussein
HusseinOP7mo ago
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "#<_Response>".] {
code: 'ERR_UNHANDLED_REJECTION'
}
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "#<_Response>".] {
code: 'ERR_UNHANDLED_REJECTION'
}
when i return redirect it doesn't do anything
Brendonovich
Brendonovich7mo ago
da heck, can you show your code now?
Hussein
HusseinOP7mo ago
actually wait i got a different problem
// routes/admin.tsx

import { load } from "~/lib/api";

export const route = {
load,
};

export default function Admin() {
return (
<main>
<h1>Admin</h1>
</main>
);
}
// routes/admin.tsx

import { load } from "~/lib/api";

export const route = {
load,
};

export default function Admin() {
return (
<main>
<h1>Admin</h1>
</main>
);
}
// lib/server.ts

export const load = async () => {
"use server";
const cookie = getCookie("chat_session");
if (!cookie) throw redirect("/");
};
// lib/server.ts

export const load = async () => {
"use server";
const cookie = getCookie("chat_session");
if (!cookie) throw redirect("/");
};
// lib/api.ts

import { action, cache } from "@solidjs/router";
import {
getChat as _getChat,
createChat as _createChat,
sendMessage as _sendMessage,
load as _load,
} from "./server";

export const getChat = cache(_getChat, "chat");
export const createChatAction = action(_createChat, "create-chat");
export const sendMessage = action(_sendMessage, "send-message");
export const load = cache(_load, "load");
// lib/api.ts

import { action, cache } from "@solidjs/router";
import {
getChat as _getChat,
createChat as _createChat,
sendMessage as _sendMessage,
load as _load,
} from "./server";

export const getChat = cache(_getChat, "chat");
export const createChatAction = action(_createChat, "create-chat");
export const sendMessage = action(_sendMessage, "send-message");
export const load = cache(_load, "load");
this is what i'm getting
Brendonovich
Brendonovich7mo ago
yea that's odd, i'm not sure
peerreynders
peerreynders7mo ago
I wonder whether the issue is that the redirect happens on the server side.
// In a server module
async function hasChatCookie() {
'use server';
return getCookie('chat_session') !== undefined;
}

// in a client module
const load = cache(async () => {
const result = await hasChatCookie();
if (!result) throw redirect('/');
return true;
}, 'load');

// in the route module
export const route = {
load: async () => load(),
};
// In a server module
async function hasChatCookie() {
'use server';
return getCookie('chat_session') !== undefined;
}

// in a client module
const load = cache(async () => {
const result = await hasChatCookie();
if (!result) throw redirect('/');
return true;
}, 'load');

// in the route module
export const route = {
load: async () => load(),
};
Given that this
async () => {
"use server";
const cookie = getCookie("chat_session");
if (!cookie) throw redirect("/");
};
async () => {
"use server";
const cookie = getCookie("chat_session");
if (!cookie) throw redirect("/");
};
is a request to /server_ that thrown response would have to be serialized and sent in a wrapper response to get back to the client (I think Ryan demonstrated that that actually works). However on client side the router would be staring at the unwrapped redirect response? I guess that could be made to work … OK… that error happens as soon as you make the load function async by extension redirects won't work. I think the idea is to only warm the cache in the load and then act on it later in the route component.
PhilippCh4ts
PhilippCh4ts6mo ago
Facing the same issue right now. At least according to the docs, this is supposed to work as confirmed in this thread, right? https://docs.solidjs.com/solid-start/advanced/auth#protected-routes I have a similar implementation for an (admin).tsx route group, see below. It logs null for the user object, but doesn’t redirect. Am I missing something?
const getCurrentUser = cache(function () {
"use server";
const event = getRequestEvent()!;
if (!event.locals.user) {
console.log(event.locals.user);
return redirect("/login");
}
return event.locals.user;
}, "current_user");

export const route = {
load() {
void getCurrentUser();
},
} satisfies RouteDefinition;
const getCurrentUser = cache(function () {
"use server";
const event = getRequestEvent()!;
if (!event.locals.user) {
console.log(event.locals.user);
return redirect("/login");
}
return event.locals.user;
}, "current_user");

export const route = {
load() {
void getCurrentUser();
},
} satisfies RouteDefinition;
peerreynders
peerreynders6mo ago
In your case it may actually start to work if you
throw redirect("/login")
throw redirect("/login")
Hussein
HusseinOP6mo ago
i do this to protect / but that doesn't work with redirecting from /login to / if you're already logged in
PhilippCh4ts
PhilippCh4ts6mo ago
Ah, sorry. Didn’t notice while copying the example. I did try both throw and return, neither works
peerreynders
peerreynders6mo ago
The point of interest then is what is in the response from the server. For this to work /login needs to be in the Location header. Two paths: - if it's not there, why isn't it - if it's there, why isn't the router navigating In your OP the issue was when you are redirecting. From what I could tell the async load tried to access the response header when it was already too late. So is it perhaps possible that you are redirecting (on the server side) outside of where the server's response can be touched or something else is modifying the Location header later?
PhilippCh4ts
PhilippCh4ts6mo ago
Sorry new to Solid altogether. Where exactly would I check the value of that header? On nativeEvent within the cache fn? Bit puzzled as to what’s happening here, I’ve tried to create a reproduction yesterday and it did in fact work as expected. So I thought maybe it had to do something with my local setup. Anyway, I’m just trying the Stackblitz example again now (not having changed anything) and the same issue is appearing https://stackblitz.com/edit/github-qbrg4x?file=src%2Froutes%2F(admin).tsx Also downloaded this project locally, and can reproduce there too. Edit: Not sure if related, but there is an error showing up in the console when refreshing the /manage-orders page.
Uncaught (in promise) Response {type: 'default', url: '', redirected: false, status: 302, ok: false, …}
Uncaught (in promise) Response {type: 'default', url: '', redirected: false, status: 302, ok: false, …}
peerreynders
peerreynders6mo ago
As soon as you add a direct dependency in the route component to a cache point that just threw a redirect, the navigation to that route will comply with the redirect as well.
// file: src/routes/(admin).tsx
import {
cache,
createAsync,
redirect,
type RouteDefinition,
type RouteSectionProps,
} from '@solidjs/router';
import { getRequestEvent } from 'solid-js/web';

const getCurrentUser = cache(async function () {
'use server';
const event = getRequestEvent()!;
if (!event.locals.user) {
throw redirect('/');
}
return event.locals.user;
}, 'current_user');

export const route = {
load() {
void getCurrentUser();
},
} satisfies RouteDefinition;

export default function Admin(props: RouteSectionProps) {
// Add this line and it prevents the navigation
// (complies with the redirect?) when there is no user.
const _user = createAsync(() => getCurrentUser());

return props.children;
}
// file: src/routes/(admin).tsx
import {
cache,
createAsync,
redirect,
type RouteDefinition,
type RouteSectionProps,
} from '@solidjs/router';
import { getRequestEvent } from 'solid-js/web';

const getCurrentUser = cache(async function () {
'use server';
const event = getRequestEvent()!;
if (!event.locals.user) {
throw redirect('/');
}
return event.locals.user;
}, 'current_user');

export const route = {
load() {
void getCurrentUser();
},
} satisfies RouteDefinition;

export default function Admin(props: RouteSectionProps) {
// Add this line and it prevents the navigation
// (complies with the redirect?) when there is no user.
const _user = createAsync(() => getCurrentUser());

return props.children;
}
Where exactly would I check the value of that header?
In this particular case I was talking about looking at the response to
http://localhost:3000/_server/?id=…%2Fsrc%2Froutes%2F(admin).tsx%3Fpick%3Droute&name=%24%24function0
http://localhost:3000/_server/?id=…%2Fsrc%2Froutes%2F(admin).tsx%3Fpick%3Droute&name=%24%24function0
in the network tab of the developer tools. That request will happen as soon as you hover over the manage-order link because of the getCurrentUser() you put in the route component's load function. Once you click on the request, you get access to the Headers tab which lists the request and response headers. The raw version of the response headers look like:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
location: /
content-type: text/javascript
Date: Tue, 28 May 2024 15:31:21 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
location: /
content-type: text/javascript
Date: Tue, 28 May 2024 15:31:21 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
Notice the Location: / —that is the result of your redirect.
PhilippCh4ts
PhilippCh4ts6mo ago
As soon as you add a direct dependency in the route component to a cache point that just threw a redirect, the navigation to that route will comply with the redirect as well.
Gosh, that was it. Everything working as expected now 🥳 Thanks so much for the pointers, that’s great info!
Want results from more Discord servers?
Add your server