How the site fits together
This chapter is the map for everything that follows: what the site is, what happens between typing a URL and seeing a page, and the one architectural idea — the server/client split — that every other chapter builds on.
The 30-second pitch
A SvelteKit 5 artist portfolio where every drawing is a one-of-a-kind item for sale. Each page splits into a server loader that safely talks to the database (Supabase) and payments (Stripe), and a Svelte component that renders the result in the browser. The UI is built with Svelte 5 runes for reactivity, styled with Tailwind CSS, and deployed to the edge on Cloudflare.
How a request flows through the site
Browser asks for /drawing/negro_1
│
▼
hooks.server.ts Runs first, on every request. Assigns a per-visitor random
"seed" cookie used to shuffle the art consistently.
│
▼
+page.server.ts Runs ONLY on the server. Queries Supabase for that notebook's
(load function) drawings and checks Stripe. Returns plain data (JSON).
│
▼
+page.svelte Runs in the browser. Receives that data as a prop and renders
the gallery with Svelte 5 runes.
│
▼
User clicks "Buy" → POST /api/checkout → Stripe Checkout page → payment
│
▼
/api/webhook marks it "sold"
and sends a confirmation emailThe single most important idea: files ending in .server.ts (and anything in a
lib/server/ folder) never get sent to the browser. That's where secret keys live. The
.svelte files are the public UI. This server/client split is the backbone of how SvelteKit
keeps secrets safe while still rendering dynamic pages.
Folder map
| Path | Role |
|---|---|
src/routes/ |
Each folder is a URL. Special filenames have special meaning (see below). |
src/routes/cart/ |
The client-rendered cart review page (+page.svelte) — no server data of its own; it reads src/lib/stores/cart.ts. |
src/routes/api/checkout/, .../cancel/, .../session-status/ |
Checkout session creation, best-effort reservation release, and public payment-status lookup — see How the shop works. |
src/routes/api/drawings/status/ |
Public sold/reserved/price lookup for a slug list — how the cart page re-checks availability before checkout. |
src/lib/components/ |
Reusable UI pieces: Feed, Gallery, PurchaseButton, ThemeToggle, BinaryClock. |
src/lib/server/ |
Server-only modules. The server/ name makes SvelteKit guarantee they never ship to the browser. Includes reservations.ts (the one place that releases a Stripe session's reservations) and checkoutSlugs.ts (the one place that reads which drawings a session covers). |
src/lib/stores/ |
App-wide shared state (theme, fullscreen, cart). |
src/lib/utils/ |
Small, pure, framework-agnostic helpers shared across components: formatTitle, formatPrice, checkoutReturn (the Stripe-return-detection helper used by both the cart page and the notebook page). |
src/hooks.server.ts |
Runs on every request before pages — sets the session seed. |
scripts/ |
Node tooling to process/upload images and seed the database — see Data pipelines. |
Special route filenames (SvelteKit convention):
| File | Meaning |
|---|---|
+page.svelte |
The page UI at this URL. |
+page.server.ts |
Server-only data loader for that page (load function). |
+server.ts |
An API endpoint (returns JSON/responses, not a page). |
+layout.svelte |
Wraps all pages in this folder and below (nav, footer, background). |
Dynamic routes use square brackets:
drawing/[slug]/→ matches/drawing/negro_1,/drawing/verde_3, … (slugis a variable)drawing/[slug]/[index]/→ matches/drawing/negro_1/3(image #3 of that notebook)
Secrets never reach the browser
The Supabase service-role key and Stripe secret key are imported only inside src/lib/server/.
Because of the server/ folder convention, SvelteKit will refuse to bundle them into client code.
The browser only ever sees the harmless rendered result.
Concept: the trust boundary between client and server — the foundation of web security. It comes back again and again in later chapters: the cart's prices are display-only (the shop), a
?success=trueURL is not proof of payment (war stories), and a cookie's presence is not a login (the owner gate).