Getting UnhandledPromiseRejection in Solid Start server function that stops dev server

I have an async function in my Solid Start app and am receiving the following error message whenever it returns an Error.
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>".
at throwUnhandledRejectionsMode (node:internal/process/promises:392:7)
at processPromiseRejections (node:internal/process/promises:475:17)
at process.processTicksAndRejections (node:internal/process/task_queues:106:32) {
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>".
at throwUnhandledRejectionsMode (node:internal/process/promises:392:7)
at processPromiseRejections (node:internal/process/promises:475:17)
at process.processTicksAndRejections (node:internal/process/task_queues:106:32) {
code: 'ERR_UNHANDLED_REJECTION'
}
Here is the function:
export async function loginOrRegister(formData: FormData) {
const username = String(formData.get("username"));
const password = String(formData.get("password"));

let error = validateUsername(username) || validatePassword(password);

if (error) {
// Promise.reject(new Error(error)). // <--- does not cause rejection, but UI does not show error message
return new Error(error); // <--- UI shows error message but dev server stops due to UnhandledPromiseRejection
}

try {
const user = await login(username, password);
const session = await getSession();
await session.update((d) => {
d.userId = user.id;
});
} catch (err) {
return err as Error;
}

throw redirect("/");
}
export async function loginOrRegister(formData: FormData) {
const username = String(formData.get("username"));
const password = String(formData.get("password"));

let error = validateUsername(username) || validatePassword(password);

if (error) {
// Promise.reject(new Error(error)). // <--- does not cause rejection, but UI does not show error message
return new Error(error); // <--- UI shows error message but dev server stops due to UnhandledPromiseRejection
}

try {
const user = await login(username, password);
const session = await getSession();
await session.update((d) => {
d.userId = user.id;
});
} catch (err) {
return err as Error;
}

throw redirect("/");
}
Not sure exactly what's going on and hoping someone can perhaps provide insight.
23 Replies
peerreynders
peerreynders2w ago
I assume it's being used like this?
const login = action((formData: FormData) => {
"use server"
loginOrRegister(formData);
}, 'login-register');
const login = action((formData: FormData) => {
"use server"
loginOrRegister(formData);
}, 'login-register');
In any other context the thrown redirect would be interpreted as an error.
MaveriX89
MaveriX89OP2w ago
That is close to correct -- I have the use server directive at the top of the file/module.
peerreynders
peerreynders2w ago
So the action is in another module that doesn't contain "use server" (which would be correct usage) and the action module simply imports loginOrRegister from the "use server" module? FYI: Because of the confusion that top level "use server" causes, it is generally discouraged. "use server" marks an RPC boundary and both query and action need to be on the client side of that boundary; they won't work correctly on the server side of that boundary.
MaveriX89
MaveriX89OP2w ago
Gotcha -- so here's the breakdown with two files/modules. This was all generated from the Create Solid CLI starter (with Drizzle option checked): First one here is ./src/api/index.ts
import { action, query } from "@solidjs/router";
import { getUser as gU, logout as l, loginOrRegister as lOR } from "./server";

export const getUser = query(gU, "user");
export const loginOrRegister = action(lOR, "loginOrRegister");
export const logout = action(l, "logout");
import { action, query } from "@solidjs/router";
import { getUser as gU, logout as l, loginOrRegister as lOR } from "./server";

export const getUser = query(gU, "user");
export const loginOrRegister = action(lOR, "loginOrRegister");
export const logout = action(l, "logout");
and the second is ./src/api/server.ts
"use server"

// ...

export async function loginOrRegister(formData: FormData) {
...
}
"use server"

// ...

export async function loginOrRegister(formData: FormData) {
...
}
I believe the above aligns with what you described
peerreynders
peerreynders2w ago
So far so good. So client-side code should only ever
import { loginOrRegister } from '~/api/index.js';
import { loginOrRegister } from '~/api/index.js';
rather than
import { loginOrRegister } from '~/api/server.js';
import { loginOrRegister } from '~/api/server.js';
I believe the latter would result in a bare RPC call without the action wrapper that interprets thrown redirects.
MaveriX89
MaveriX89OP2w ago
That's good to know. Yeah, it's being used client side in tthe following manner:
import { useSubmission, type RouteSectionProps } from "@solidjs/router";
import { Show } from "solid-js";
import { loginOrRegister } from "~/api"; // <--- here is the import
import Logo from "../assets/logo_login.svg";
import { TextInput } from "~/components/TextInput";
import { Button } from "@kobalte/core/button";

export default function Login(props: RouteSectionProps) {
const loggingIn = useSubmission(loginOrRegister);

return (
<div class="h-full flex flex-col items-center justify-center gap-2">
<img src={Logo} width="200" />
<form action={loginOrRegister} method="post" class="flex flex-col gap-4">
<input type="hidden" name="redirectTo" value={props.params.redirectTo ?? "/"} />
<TextInput name="username" label="Username" />
<TextInput name="password" label="Password" type="password" />
<Button class="button" type="submit">
Login
</Button>
</form>
<Show when={loggingIn.result}>
<p style={{ color: "red" }} role="alert" id="error-message">
{loggingIn.result!.message}
</p>
</Show>
</div>
);
}
import { useSubmission, type RouteSectionProps } from "@solidjs/router";
import { Show } from "solid-js";
import { loginOrRegister } from "~/api"; // <--- here is the import
import Logo from "../assets/logo_login.svg";
import { TextInput } from "~/components/TextInput";
import { Button } from "@kobalte/core/button";

export default function Login(props: RouteSectionProps) {
const loggingIn = useSubmission(loginOrRegister);

return (
<div class="h-full flex flex-col items-center justify-center gap-2">
<img src={Logo} width="200" />
<form action={loginOrRegister} method="post" class="flex flex-col gap-4">
<input type="hidden" name="redirectTo" value={props.params.redirectTo ?? "/"} />
<TextInput name="username" label="Username" />
<TextInput name="password" label="Password" type="password" />
<Button class="button" type="submit">
Login
</Button>
</form>
<Show when={loggingIn.result}>
<p style={{ color: "red" }} role="alert" id="error-message">
{loggingIn.result!.message}
</p>
</Show>
</div>
);
}
Just not sure why when I invoke the error path (e.g. submitting without username and password) while in dev mode with my dev server running, the failure path on the server side chokes with the unhandled exception and shuts down my dev server which is not a graceful way to handle that. Seems like the wrong behavior to me. Not sure if it's something that I'm doing wrong from a workflow perspective / component design.
peerreynders
peerreynders2w ago
Have you been able to get a minimal logHello to work? Sometimes other code interferes. FYI: https://discord.com/channels/722131463138705510/1307001733905645599 but we covered that. Another thought: You are redirecting to '/' ; is it possible there is a function in load for src/routes/index.tsxthat misbehaves once the login has been successful?
MaveriX89
MaveriX89OP2w ago
Ah, that's funny -- I think that issue is the same exact problem I'm facing. In my case, the success path works fine without a problem. Haven't noticed any issues when logging in successfully. It only is the path where I return a new Error() in the code path for logging in on the server. Judging from the other post, could the problem be in me using getUser in some premature way? Below is my app's root layout
// @refresh reload
import { Router, A, createAsync, RouteSectionProps } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { ParentProps, Show, Suspense } from "solid-js";
import { getUser } from "~/api";
import Logo from "./assets/logo.svg";
import { CircleUser } from "lucide-solid";
import "./app.css";

type User = {
readonly username: string;
}

type NavbarProps = {
readonly user: User;
}

const Navbar = (props: NavbarProps) => {
return (
<header class="text-white bg-slate-700 p-1 flex flex-row gap-2">
<div class="flex gap-1">
<img src={Logo} width="20" />
<p>Time Tracker</p>
</div>
<nav class="flex flex-row">
<A href="/">Dashboard</A>
</nav>
<div class="ml-auto flex items-center">
<CircleUser class="ml-auto" height={18} stroke-width={1} />
<p>{props.user.username}</p>
</div>
</header>
);
};

const MainContent = (props: ParentProps<{ class: string }>) => (
<main class={props.class}>
<Suspense>{props.children}</Suspense>
</main>
)

const RootLayout = (props: RouteSectionProps<unknown>) => {
const user = createAsync(async () => getUser(), { deferStream: true });

return (
<Suspense>
<Show when={user()} fallback={<MainContent class="h-screen">{props.children}</MainContent>}>
<div class="h-screen grid grid-rows-[auto_1fr]">
<Navbar user={user() as User} />
<MainContent class="h-full">{props.children}</MainContent>
</div>
</Show>
</Suspense>
)
};

export default function App() {
return (
<Router root={RootLayout}>
<FileRoutes />
</Router>
);
}
// @refresh reload
import { Router, A, createAsync, RouteSectionProps } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { ParentProps, Show, Suspense } from "solid-js";
import { getUser } from "~/api";
import Logo from "./assets/logo.svg";
import { CircleUser } from "lucide-solid";
import "./app.css";

type User = {
readonly username: string;
}

type NavbarProps = {
readonly user: User;
}

const Navbar = (props: NavbarProps) => {
return (
<header class="text-white bg-slate-700 p-1 flex flex-row gap-2">
<div class="flex gap-1">
<img src={Logo} width="20" />
<p>Time Tracker</p>
</div>
<nav class="flex flex-row">
<A href="/">Dashboard</A>
</nav>
<div class="ml-auto flex items-center">
<CircleUser class="ml-auto" height={18} stroke-width={1} />
<p>{props.user.username}</p>
</div>
</header>
);
};

const MainContent = (props: ParentProps<{ class: string }>) => (
<main class={props.class}>
<Suspense>{props.children}</Suspense>
</main>
)

const RootLayout = (props: RouteSectionProps<unknown>) => {
const user = createAsync(async () => getUser(), { deferStream: true });

return (
<Suspense>
<Show when={user()} fallback={<MainContent class="h-screen">{props.children}</MainContent>}>
<div class="h-screen grid grid-rows-[auto_1fr]">
<Navbar user={user() as User} />
<MainContent class="h-full">{props.children}</MainContent>
</div>
</Show>
</Suspense>
)
};

export default function App() {
return (
<Router root={RootLayout}>
<FileRoutes />
</Router>
);
}
Does anything look unexpected here?
peerreynders
peerreynders2w ago
Nothing jumps out at me. Perhaps open the network tab and look the server_ requests. The RPC responsible for the server croaking would likely not send a response. The request will have an X-Server-Id header with something like
/bla/bla/src/api/server.ts#$$function5
/bla/bla/src/api/server.ts#$$function5
That may give you a path to determine whether or not the loginOrRegister RPC is the actual culprit. Then comment stuff out to see when it stops to break.
MaveriX89
MaveriX89OP2w ago
This is what I see from the response from server_
;0x0000085a;
((self.$R = self.$R || {})["server-fn:0"] = [],
($R => $R[0] = {
"user[]": $R[1] = ($R[2] = (s, f, p) => ((p = new Promise( (a, b) => {
s = a,
f = b
}
)).s = s,
p.f = f,
p))(),
_$value: $R[3] = Object.assign(new Error("Usernames must be at least 3 characters long"), {
stack: "Error: Usernames must be at least 3 characters long\n at loginOrRegister2 (/Users/dsanchez/Developer/github/dsanchez/time-tracker/src/api/server.ts:66:12)\n at eval (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-fns-runtime.js:29:19)\n at AsyncLocalStorage.run (node:internal/async_local_storage/async_hooks:91:14)\n at Module.provideRequestEvent (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/solid-js/web/storage/dist/storage.js:7:14)\n at Object.apply (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-fns-runtime.js:28:36)\n at eval (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-handler.js:110:14)\n at AsyncLocalStorage.run (node:internal/async_local_storage/async_hooks:91:14)\n at Module.provideRequestEvent (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/solid-js/web/storage/dist/storage.js:7:14)\n at handleServerFunction (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-handler.js:103:46)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async _callHandler (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/h3/dist/index.mjs:1837:16)\n at async file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/h3/dist/index.mjs:1978:19\n at async Object.callAsync (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/unctx/dist/index.mjs:72:16)\n at async Server.toNodeHandle (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/h3/dist/index.mjs:2270:7)"
})
})($R["server-fn:0"]));
0x000000e3;
($R => ($R[9] = (p, d) => {
p.f(d),
p.status = "failure",
p.value = d;
delete p.s;
delete p.f
}
)($R[1], $R[4] = new Response(null,$R[5] = {
headers: $R[6] = new Headers($R[7] = [$R[8] = ["location", "/login"]]),
status: 302,
statusText: ""
})))($R["server-fn:0"])
;0x0000085a;
((self.$R = self.$R || {})["server-fn:0"] = [],
($R => $R[0] = {
"user[]": $R[1] = ($R[2] = (s, f, p) => ((p = new Promise( (a, b) => {
s = a,
f = b
}
)).s = s,
p.f = f,
p))(),
_$value: $R[3] = Object.assign(new Error("Usernames must be at least 3 characters long"), {
stack: "Error: Usernames must be at least 3 characters long\n at loginOrRegister2 (/Users/dsanchez/Developer/github/dsanchez/time-tracker/src/api/server.ts:66:12)\n at eval (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-fns-runtime.js:29:19)\n at AsyncLocalStorage.run (node:internal/async_local_storage/async_hooks:91:14)\n at Module.provideRequestEvent (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/solid-js/web/storage/dist/storage.js:7:14)\n at Object.apply (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-fns-runtime.js:28:36)\n at eval (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-handler.js:110:14)\n at AsyncLocalStorage.run (node:internal/async_local_storage/async_hooks:91:14)\n at Module.provideRequestEvent (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/solid-js/web/storage/dist/storage.js:7:14)\n at handleServerFunction (/Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/@solidjs/start/dist/runtime/server-handler.js:103:46)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async _callHandler (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/h3/dist/index.mjs:1837:16)\n at async file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/h3/dist/index.mjs:1978:19\n at async Object.callAsync (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/unctx/dist/index.mjs:72:16)\n at async Server.toNodeHandle (file:///Users/dsanchez/Developer/github/dsanchez/time-tracker/node_modules/h3/dist/index.mjs:2270:7)"
})
})($R["server-fn:0"]));
0x000000e3;
($R => ($R[9] = (p, d) => {
p.f(d),
p.status = "failure",
p.value = d;
delete p.s;
delete p.f
}
)($R[1], $R[4] = new Response(null,$R[5] = {
headers: $R[6] = new Headers($R[7] = [$R[8] = ["location", "/login"]]),
status: 302,
statusText: ""
})))($R["server-fn:0"])
peerreynders
peerreynders2w ago
Given that there is a response, why do you suspect that it is this request that is giving the server problems (sorry if I'm being slow). Just for comparison
0x00000714;
((self.$R = self.$R || {})['server-fn:3'] = []),
(($R) =>
($R[0] = {
'home-authenticated[]': ($R[1] = ($R[2] = (s, f, p) => (
((p = new Promise((a, b) => {
(s = a), (f = b);
})).s = s),
(p.f = f),
p
))()),
_$value: ($R[3] = Object.assign(
new Error('Password must be at least 6 characters.'),
{
stack:
'Error: Password must be at least 6 characters.\n at $$function2 (really long stack trace describing the location where the error was created)',
}
)),
}))($R['server-fn:3']);
0x00000071;
(($R) =>
($R[4] = (p, d) => {
p.s(d), (p.status = 'success'), (p.value = d);
delete p.s;
delete p.f;
})($R[1], void 0))($R['server-fn:3']);
0x00000714;
((self.$R = self.$R || {})['server-fn:3'] = []),
(($R) =>
($R[0] = {
'home-authenticated[]': ($R[1] = ($R[2] = (s, f, p) => (
((p = new Promise((a, b) => {
(s = a), (f = b);
})).s = s),
(p.f = f),
p
))()),
_$value: ($R[3] = Object.assign(
new Error('Password must be at least 6 characters.'),
{
stack:
'Error: Password must be at least 6 characters.\n at $$function2 (really long stack trace describing the location where the error was created)',
}
)),
}))($R['server-fn:3']);
0x00000071;
(($R) =>
($R[4] = (p, d) => {
p.s(d), (p.status = 'success'), (p.value = d);
delete p.s;
delete p.f;
})($R[1], void 0))($R['server-fn:3']);
This is the payload from similar code where an Error is returned (rather than thrown) as a result of the server side function. Though perhaps your p.status = "failure", means something went sideways. Looking at this it looks like a route guard (redirect to '/login') is being triggered as well
0x000000e3;
($R => ($R[9] = (p, d) => {
p.f(d),
p.status = "failure",
p.value = d;
delete p.s;
delete p.f
}
)($R[1], $R[4] = new Response(null,$R[5] = {
headers: $R[6] = new Headers($R[7] = [$R[8] = ["location", "/login"]]),
status: 302,
statusText: ""
})))($R["server-fn:0"])
0x000000e3;
($R => ($R[9] = (p, d) => {
p.f(d),
p.status = "failure",
p.value = d;
delete p.s;
delete p.f
}
)($R[1], $R[4] = new Response(null,$R[5] = {
headers: $R[6] = new Headers($R[7] = [$R[8] = ["location", "/login"]]),
status: 302,
statusText: ""
})))($R["server-fn:0"])
… maybe something in there is responsible for the p.status = "failure"? BTW: in your OP I assumed that the message was from the server side (node:internal and all that). Was I wrong and you actually got that from the client side console?
MaveriX89
MaveriX89OP2w ago
I'm all new to this myself in using Solid Start for the first time -- apologies if I'm not explaining things correctly. Basically, when I comment out that line where I return the Error object in my log in server function, the dev server doesn't croak and die. When I keep it and see it run, I actually see the error message appear on my UI that the Error returned, but the dev server dies once it appears. I took that as an indication that something perhaps went wrong on the server side and not client side 🤔 Yeah, this is all happening on my /login route page. I'm basically submitting an empty form and testing my failure/error path in my code.
peerreynders
peerreynders2w ago
As you can see here: https://github.com/solidjs-community/strello/blob/9c9ae973d96cc045914e696757a1b5f31efc6fa1/src/lib/index.ts#L22 returning an Error instance shouldn't cause any problems. There doesn't seem to be anything untoward with this part
0x0000085a;
((self.$R = self.$R || {})['server-fn:0'] = []),
(($R) =>
($R[0] = {
'user[]': ($R[1] = ($R[2] = (s, f, p) => (
((p = new Promise((a, b) => {
(s = a), (f = b);
})).s = s),
(p.f = f),
p
))()),
_$value: ($R[3] = Object.assign(
new Error('Usernames must be at least 3 characters long'),
{
stack:
'Error: Usernames must be at least 3 characters long\n at loginOrRegister2 (really long stack trace describing the location where the error was created)',
}
)),
}))($R['server-fn:0']);
0x0000085a;
((self.$R = self.$R || {})['server-fn:0'] = []),
(($R) =>
($R[0] = {
'user[]': ($R[1] = ($R[2] = (s, f, p) => (
((p = new Promise((a, b) => {
(s = a), (f = b);
})).s = s),
(p.f = f),
p
))()),
_$value: ($R[3] = Object.assign(
new Error('Usernames must be at least 3 characters long'),
{
stack:
'Error: Usernames must be at least 3 characters long\n at loginOrRegister2 (really long stack trace describing the location where the error was created)',
}
)),
}))($R['server-fn:0']);
It's the second part that is unexpected. The fact that removing the Error fixes things is just weird. I suspect something else is going on.
GitHub
strello/src/lib/index.ts at 9c9ae973d96cc045914e696757a1b5f31efc6fa...
Contribute to solidjs-community/strello development by creating an account on GitHub.
peerreynders
peerreynders2w ago
Is there anywhere in your code where you redirect('/login')?
peerreynders
peerreynders2w ago
I'm wondering whether this is problematic:
const RootLayout = (props: RouteSectionProps<unknown>) => {
const user = createAsync(async () => getUser(), { deferStream: true });
const RootLayout = (props: RouteSectionProps<unknown>) => {
const user = createAsync(async () => getUser(), { deferStream: true });
If you are throwing a redirect('/login') when navigating to /login … The typical pattern is to place a route guard on /login to redirect('/') for already authenticated visitors. https://github.com/solidjs-community/strello/blob/9c9ae973d96cc045914e696757a1b5f31efc6fa1/src/routes/login.tsx#L11 https://github.com/solidjs-community/strello/blob/9c9ae973d96cc045914e696757a1b5f31efc6fa1/src/lib/index.ts#L41-L49 Any page requiring authentication uses preload: https://github.com/solidjs-community/strello/blob/9c9ae973d96cc045914e696757a1b5f31efc6fa1/src/routes/index.tsx#L62 https://github.com/solidjs-community/strello/blob/9c9ae973d96cc045914e696757a1b5f31efc6fa1/src/lib/index.ts#L7-L14 to getUser() which will result in a redirect('/login') if there is no authentication.
GitHub
strello/src/routes/index.tsx at 9c9ae973d96cc045914e696757a1b5f31ef...
Contribute to solidjs-community/strello development by creating an account on GitHub.
GitHub
strello/src/routes/login.tsx at 9c9ae973d96cc045914e696757a1b5f31ef...
Contribute to solidjs-community/strello development by creating an account on GitHub.
GitHub
strello/src/lib/index.ts at 9c9ae973d96cc045914e696757a1b5f31efc6fa...
Contribute to solidjs-community/strello development by creating an account on GitHub.
peerreynders
peerreynders2w ago
GitHub
strello/src/components/Layout.tsx at 9c9ae973d96cc045914e696757a1b5...
Contribute to solidjs-community/strello development by creating an account on GitHub.
MaveriX89
MaveriX89OP2w ago
Yeah, it's an interesting problem...it's unfortunately blocking me from using Solid Start because of it. I may just pivot to doing a vanilla Solid SPA with an Elysia backend server if I'm not able to resolve the issue Only downside to going vanilla like that is that I'm not able to make use of the Data APIs within @solid/router if I'm not mistaken
peerreynders
peerreynders2w ago
What you lose is SSR and RPC ("use server"); if you use solid-router you can still use query and createAsync which will move to the core with 2.0.
peerreynders
peerreynders2w ago
FYI: Here is an example of a SolidStart drizzle app: https://github.com/stefan-karger/kreativ-hub It had some teething issues https://discord.com/channels/722131463138705510/722131463889223772/1304368665763774514 If you have a look right now it only uses the function scope form of "use server".
GitHub
GitHub - stefan-karger/kreativ-hub: A creative management platform ...
A creative management platform built with SolidJS. - stefan-karger/kreativ-hub
MaveriX89
MaveriX89OP18h ago
@peerreynders btw, do I also lose the ability to use action from Solid Router in my Elysia + Solid SPA approach?
peerreynders
peerreynders18h ago
actions are entirely client side. With SPA they just tend to be more verbose than with "use server" and of course single flight mutations can't be supported.
MaveriX89
MaveriX89OP18h ago
Thanks for all your help and input in this thread. You've been incredibly invaluable. Yeah, I figured that I would lose single flight mutations in this road. That's a bullet I'm gonna have to bite. I've been fighting Solid Start way too much over these past few days over authenticated routes and how to properly manage them. Just my two cents, I feel that the documentation needs to be improved in perhaps adding examples around this use-case of properly handling protected routes within Solid Start.
peerreynders
peerreynders18h ago
adding examples around this use-case of properly handling protected routes within Solid Start.
For that I'm mostly guided by how strello implements it. - getUser at the top level layout which throws a redirect('/login') if there is no active authentication. - while redirectIfLoggedIn on the /login route preload throws a redirect('/') if there is active authentication. This builds on the approach used in the with-auth example.
GitHub
GitHub - solidjs-community/strello
Contribute to solidjs-community/strello development by creating an account on GitHub.
GitHub
solid-start/examples/with-auth at main · solidjs/solid-start
SolidStart, the Solid app framework. Contribute to solidjs/solid-start development by creating an account on GitHub.
GitHub
strello/src/components/Layout.tsx at 9c9ae973d96cc045914e696757a1b5...
Contribute to solidjs-community/strello development by creating an account on GitHub.
GitHub
strello/src/routes/login.tsx at 9c9ae973d96cc045914e696757a1b5f31ef...
Contribute to solidjs-community/strello development by creating an account on GitHub.

Did you find this page helpful?