S SvelteKurs
M9 · Laden & Mutieren

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 ts
// 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 ruby
# 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
end

Serialisierbarkeit

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 ts
// 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.

depends + invalidate ts
// +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.ts läuft nur serverseitig — DB, Secrets, locals.
  • Rückgabewerte müssen serialisierbar sein (keine Funktionen/Methoden).
  • +layout.server.ts teilt Daten mit allen Seiten darunter; parent() holt sie ab.
  • depends/invalidate steuern 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?