load — server (+page.server.ts)
Wenn Datenbank, Secrets oder der eingeloggte User im Spiel sind: Server-only load-Funktionen, die niemals im Browser landen.
Nur auf dem Server
Sobald du eine Datenbank abfragst, ein API-Secret verwendest oder auf den eingeloggten User
zugreifst, gehört der Code in eine +page.server.ts. Dieser Code läuft ausschließlich auf dem Server und wird nie zum Browser-Bundle hinzugefügt —
deine Zugangsdaten bleiben also sicher.
// src/routes/dashboard/+page.server.ts
import { db, schema } from '$lib/server/db';
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
// locals.user wird in hooks.server.ts gesetzt (wie current_user)
if (!locals.user) throw redirect(303, '/login');
const notes = await db.select().from(schema.notes);
return { user: locals.user, notes };
};🛤️ Rails-Vergleich: locals ≈ current_user, load ≈ Action mit before_action
locals ist das serverseitige Ablagefach pro Request — vergleichbar damit, wie du
in Rails current_user über einen before_action setzt. Der Guard if (!locals.user) throw redirect(303, '/login') entspricht deinem authenticate_user!. Und der DB-Zugriff db.select()... ist das
Pendant zu current_user.notes — bewusst nur serverseitig.
# Rails: before_action + starke Trennung Server/Client
class DashboardController < ApplicationController
before_action :authenticate_user! # ~ Guard mit locals.user
def show
@user = current_user # ~ locals.user
@notes = current_user.notes # DB-Zugriff nur auf dem Server
end
endSerialisierbarkeit
Weil server-load auf dem Server läuft und das Ergebnis über das Netz zum Browser geschickt
wird, muss der Rückgabewert serialisierbar sein. SvelteKit nutzt dafür devalue, das etwas mehr kann als JSON (z. B. Date, Map, Set, BigInt). Nicht erlaubt sind Funktionen und Klassen-Instanzen mit Methoden.
⚠️ Häufige Stolperfalle
Gib keine ganzen ORM-Modell-Instanzen oder Funktionen zurück, sondern nur reine Daten
(Plain Objects/Arrays). Universelles +page.ts hat diese Einschränkung nicht,
weil sein Ergebnis im selben Kontext bleibt.
Geteilte Daten mit +layout.server.ts
Daten, die auf vielen Seiten gebraucht werden — allen voran der eingeloggte User — lädst du
einmal in einer +layout.server.ts. Ihr Ergebnis steht in data jeder
Seite darunter zur Verfügung (und über await parent() auch in tieferen
load-Funktionen).
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
// Läuft für ALLE Seiten darunter — ideal für den eingeloggten User.
export const load: LayoutServerLoad = async ({ locals }) => {
return { user: locals.user };
};ℹ️ parent()
In einer tieferliegenden load holst du dir Layout-Daten mit const { user } = await parent() — nützlich, um Elternwerte
weiterzuverwenden, ohne sie erneut zu laden.
Reaktives Neuladen: depends & invalidate
SvelteKit merkt sich automatisch, von welchen URLs (via fetch) und params eine load-Funktion abhängt, und lädt sie bei Bedarf neu. Willst du selbst
bestimmen, wann neu geladen wird, markierst du mit depends('name') eine benannte
Abhängigkeit und stößt sie später per invalidate('name') an. invalidateAll() lädt alle aktiven load-Funktionen neu.
// +page.server.ts
export const load = async ({ fetch, depends }) => {
depends('app:notes'); // benannte Abhängigkeit
const res = await fetch('/api/notes');
return { notes: await res.json() };
};
// irgendwo in einer Komponente:
import { invalidate } from '$app/navigation';
await invalidate('app:notes'); // -> load läuft erneut, data ist frisch💡 Nach Mutationen
Nach einer Form Action ruft SvelteKit die zuständigen load-Funktionen ohnehin automatisch
erneut auf — du brauchst invalidate nur für Fälle außerhalb dieses Zyklus.
Zusammenfassung
💡 Das Wichtigste
+page.server.tsläuft nur serverseitig — DB, Secrets,locals.- Rückgabewerte müssen serialisierbar sein (keine Funktionen/Methoden).
+layout.server.tsteilt Daten mit allen Seiten darunter;parent()holt sie ab.depends/invalidatesteuern gezieltes Neuladen.
🎯 Probier es selbst
Schreibe eine +layout.server.ts, die { user: locals.user } zurückgibt, und gib in einer darunterliegenden +page.svelte data.user?.username aus. Überlege: Warum darf hier kein +page.ts stehen?