S
SolidJS2mo 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
Hussein2mo ago
it crashes the whole server
Brendonovich
Brendonovich2mo ago
redirect is for use within cache/action, try extracting that async function into a cache and calling it in load
Hussein
Hussein2mo 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
Brendonovich2mo ago
da heck, can you show your code now?
Hussein
Hussein2mo 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
Brendonovich2mo ago
yea that's odd, i'm not sure
peerreynders
peerreynders2mo 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.
rppld
rppld2mo 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
peerreynders2mo ago
In your case it may actually start to work if you
throw redirect("/login")
throw redirect("/login")
Hussein
Hussein2mo ago
i do this to protect / but that doesn't work with redirecting from /login to / if you're already logged in
rppld
rppld2mo ago
Ah, sorry. Didn’t notice while copying the example. I did try both throw and return, neither works
peerreynders
peerreynders2mo 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?
rppld
rppld2mo 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
peerreynders2mo 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.
rppld
rppld2mo 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
More Posts
Difficulty updating deeply nested store: produce() typed as Partial<T>I'm experiencing some difficulties updating deeply nested state within a store, specifically with thWhere can I find any documentation about `RouteDefinition`?I can see (in `dist/types.d.ts`) what properties it has, but I don't know what some of them are for.how do i use Drizzle?I'm trying to use Drizzle db on the server only and its giving me "Uncaught TypeError: promisify is Problems changing the value of a reactive signal returned from a functionI was testing what would happen if I changed the entire value of the board, not the squares but the OptionalParams in route not workingI'm not getting the expected result using optional params - routes -- users ---- [[id]].tsx ---- inReactivity resulting from updates in a websocket event listenerI am struggling to understand why setting a signal in an event listener is not driving re-rendering How do i conditionally add something to <head> based on what createAsync returns?I need to wait for a resource coming from `createAsync` so then i can decide whether i should add goHow do you delete an entry in an array when using `createStore`In the following documentation, this case is not presented: https://docs.solidjs.com/guides/complex-Uncaught ReferenceError: React is not definedHey there! Please help. I can't figure out this error. I have a tiny solid-js project that works okWhat do I do if I have to wait for things to happen, meanwhile define extra state or whatthis piece of code come up as crucial functionality all over the place for me, for an interactive ap