loader
export const routes = createRoutesFromElements( <> <Route loader={() => Response.redirect("/other")} path="/" element={<Home />} /> <Route path="/other" element={<Other />} /> </>);
export default function Home() { const form = useSubmission(login); return ( <Show when={form.error}> {(error: Accessor<Error>) => <p>{error().message}</p>} </Show> );}
async function redirect() { "use server"; // how?? redirect("/other");}
import { isDev } from "solid-js/web";console.log({ isDev });
false
const login = action(async (formData: FormData) => { ("use server"); const username = formData.get("username"); const password = formData.get("password"); if (typeof username === "string" && typeof password === "string") { const foundUser = await db.query.user.findFirst({ where: eq(user.username, username), }); if (!foundUser || !(await verify(foundUser.password, password))) return new Error("Wrong username or password"); const session = await lucia.createSession(foundUser.id, {}); const sessionCookie = lucia.createSessionCookie(session.id); setCookie( sessionCookie.name, sessionCookie.value, sessionCookie.attributes ); // this is happening before the cookie gets set throw redirect("/"); }}, "login");
const getUser = cache(async () => { "use server"; const sessionId = getCookie(lucia.sessionCookieName); if (!sessionId) throw redirect("/login"); const { user } = await lucia.validateSession(sessionId); if (!user) throw redirect("/login"); return user;}, "user");export const route = { load: () => getUser(),};
export default function Home() { const user = createAsync(() => getUser()); const shouldRedirect = createMemo(() => !user()); return <Suspense>{shouldRedirect() && <Navigate href="/login" />}</Suspense>;}
vinxi build
import { cache, createAsync, Route, Router } from "@solidjs/router";import { Suspense } from "solid-js";export function App({ url }: { url?: string }) { return ( <Router url={url}> <Route path="/" component={() => ( <> Hello World <a style={{ display: "block", "margin-top": "1em" }} href="/other"> Other </a> </> )} /> <Route path="/other" component={() => { const get = cache(async () => { const res = await fetch( "https://jsonplaceholder.typicode.com/todos/1" ); return res.json(); }, "data"); const data = createAsync(() => get()); return ( <> Other <Suspense fallback={<p>Loading...</p>}> <pre>{JSON.stringify(data())}</pre> </Suspense> <a style={{ display: "block", "margin-top": "1em" }} href="/"> Home </a> </> ); }} /> </Router> );}
index.tsx
import { User } from "lucia";declare module "@solidjs/start" { export interface RequestEventLocals { user?: User; }}
<Router preload>
export const route = { load: async () => { "use server"; const cookie = getCookie("chat_session"); if (!cookie) throw redirect("/"); },};
import { cache, createAsync } from "@solidjs/router";import { db } from "~/lib/db";const getData = cache(() => { return db.query.chat.findMany();}, "data");export const route = { load: getData };export default function Home() { const data = createAsync(() => getData()); return <p>{data()?.length}</p>;}
lib/db
import Database from "better-sqlite3";import { drizzle } from "drizzle-orm/better-sqlite3";import * as schema from "./schema";import { migrate } from "drizzle-orm/better-sqlite3/migrator";export const db = drizzle(new Database("db.sqlite"), { schema });migrate(db, { migrationsFolder: "drizzle" });
createAsync
useHead
const getChat = cache(async () => { return { messages: [{ text: "hi", author: "me" }] };}, "chat");export default function Contact() { const chat = createAsync(() => { return getChat(); }); if (!chat()) useHead({ tag: "script", id: "recaptcha", setting: { close: true }, props: { src: "https://www.google.com/recaptcha/api.js", async: true, defer: true, }, });
const [user] = createResource(async () => { const me = await trpc.me.query(); return me;});
user
null
/login