Protected Routes in SvelteKit (Don't Use +layout.server.ts)
I just found out that I’ve been implementing protected routes wrong in my SvelteKit apps. Like many people, I assumed a certain hierarchy to the execution of load functions. That is to say I thought that if a load function in +layout.server.ts
returns an error, it will short circuit preventing the rest of the load functions down the hierarchy from even running. I was wrong.
In SvelteKit, load
functions run in parallel. That’s great for performance. Also very counterintuitive. Turns out there is no hierarchy. So what do we do ?
There is one function that always runs before all the load functions or even the form actions. It’s the handle function in hooks.server.ts
. We can protect our routes there.
import { redirect } from "@sveltejs/kit";
import { svelteKitHandler } from "better-auth/svelte-kit";
import { auth } from "$lib/server/auth";
export async function handle({ event, resolve }) {
const session = await auth.api.getSession({
headers: event.request.headers,
});
event.locals.session = session;
if (event.url.pathname.includes("admin") && !session?.user)
throw redirect(303, "/");
return svelteKitHandler({ event, resolve, auth });
}
Since SvelteKit allows us to use parent data, why don’t we just add await event.parent
in the beginning of any load function we want to protect ?
That will create the waterfall of requests that SvelteKit was trying to protect us from. It also makes the code quite difficult to reason about. So I recommend the hooks based solution.