# SvelteKit + Supabase: per-request server client via hooks.server.ts

SvelteKit renders on both sides, so agents pick one client and use it everywhere. That breaks in both directions: the browser client has no cookies on the server, and a shared server client leaks sessions between requests.

## Checkable procedure

1. Install `@supabase/supabase-js` and `@supabase/ssr`.
2. In `hooks.server.ts`, create the server client per request with `createServerClient`, wiring its cookie interface to SvelteKit's `event.cookies`: `getAll` from `event.cookies.getAll()`, `setAll` writing back through `event.cookies.set()`.
3. Expose the session/user on `event.locals` in the handle function so `load` functions and server routes can read auth state without rebuilding clients.
4. In `+layout.ts` (the universal load), create the browser client with `createBrowserClient` for client-side navigation. Server `load` functions (`+page.server.ts`) use the request-scoped server client from `locals`.
5. Verify with `supabase.auth.getClaims()` on the server before trusting who the user is. `getSession()` on the server reads an unverified cookie.

## The leak to check for

A client created at module scope in `$lib/` and imported by `+page.server.ts` files is shared across requests. Symptoms are intermittent: user A occasionally sees user B's data, usually under load when requests overlap. The fix is always the same, create it inside the request handler.

## Quick test

Two browsers, two users, hit a server-loaded page that shows the email at the same time. Then expire the access token and confirm navigation still works, the hooks refresh keeps the session alive.