fix: les 6

This commit is contained in:
2026-03-11 14:07:00 +01:00
parent d5066021ab
commit 9ffdecf2c4
117 changed files with 13198 additions and 5194 deletions

View File

@@ -1,372 +1,305 @@
# Les 8: Database Principles
# Les 8: Supabase: Auth & CRUD
---
## Hoofdstuk
**Deel 2: Technical Foundations** (Les 5-9)
**Deel 2: Technical Foundations** (Les 4-8)
## Beschrijving
Leer de basisprincipes van relationele databases voordat we Supabase gaan gebruiken. Begrijp tabellen, relaties, keys en normalisatie - essentiële kennis voor elke developer.
Supabase Authentication en CRUD operaties. Implementeer email/password auth, JWT tokens, Row Level Security (RLS) policies, realtime subscriptions en volledige CRUD functionaliteit in je full-stack app.
---
## Te Behandelen
### Wat is een Relationele Database?
### Wat is Supabase?
**Een database is:** Een georganiseerde verzameling van data.
**Supabase = Database + Auth in één**
- PostgreSQL database (gratis tier: 500MB)
- Ingebouwde authenticatie
- Real-time subscriptions
- File storage
- Auto-generated API
**Relationeel betekent:** Data is opgeslagen in tabellen die aan elkaar gerelateerd zijn.
**Vergelijk het met Excel:**
- Database = Excel workbook
- Tabel = Excel sheet
- Kolom = Excel kolom (field)
- Rij = Excel rij (record)
**Waarom Supabase voor beginners:**
- Geen eigen server nodig
- Visuele Table Editor (geen SQL kennis nodig)
- Simpele JavaScript SDK
- Gratis tier is ruim voldoende
---
### Tabellen, Kolommen en Rijen
### Supabase Project Aanmaken
**Voorbeeld: Users tabel**
**Stap 1:** Ga naar [supabase.com](https://supabase.com) en maak account
| id | name | email | created_at |
|----|------|-------|------------|
| 1 | Tim | tim@email.com | 2024-01-15 |
| 2 | Anna | anna@email.com | 2024-01-16 |
| 3 | Jan | jan@email.com | 2024-01-17 |
**Stap 2:** Klik "New Project"
- Naam: `todo-app`
- Database Password: (bewaar deze!)
- Region: `West EU (Frankfurt)` (dichtst bij NL)
**Terminologie:**
- **Tabel:** users
- **Kolommen:** id, name, email, created_at
- **Rijen:** 3 records (Tim, Anna, Jan)
- **Cell:** Eén specifieke waarde (bijv. "tim@email.com")
**Stap 3:** Wacht ~2 minuten tot project klaar is
**Stap 4:** Ga naar Settings → API en kopieer:
- `Project URL`
- `anon public` key
---
### Data Types
### Je Database Schema Implementeren
Elke kolom heeft een type:
In Les 7 heb je een database schema ontworpen. Nu gaan we dat implementeren!
| Type | Beschrijving | Voorbeeld |
|------|--------------|-----------|
| `text` / `varchar` | Tekst | "Tim", "Hello world" |
| `integer` / `int` | Hele getallen | 1, 42, -5 |
| `decimal` / `numeric` | Decimalen | 19.99, 3.14 |
| `boolean` | True/False | true, false |
| `timestamp` | Datum + tijd | 2024-01-15 14:30:00 |
| `uuid` | Unieke identifier | a1b2c3d4-e5f6-... |
**In Supabase Dashboard → Table Editor:**
**Kies het juiste type:**
- Prijs? → `decimal` (niet `integer`, want centen)
- Is actief? → `boolean`
- Naam? → `text`
- Aantal? → `integer`
1. Klik "New Table"
2. Gebruik je schema uit Les 7
3. Voeg kolommen toe met de juiste types
4. Definieer Primary Keys en Foreign Keys
**Voorbeeld: todos tabel**
| Name | Type | Default | Primary |
|------|------|---------|---------|
| id | int8 | - | ✓ (auto) |
| title | text | - | |
| completed | bool | false | |
| created_at | timestamptz | now() | |
---
### Primary Keys
### Environment Variables
**Wat:** Een kolom die elke rij UNIEK identificeert.
**Wat zijn environment variables?**
- Configuratie die NIET in je code hoort
- API keys, database URLs, secrets
- Verschillend per omgeving (lokaal vs productie)
**Regels:**
- Moet uniek zijn per rij
- Mag nooit NULL zijn
- Verandert nooit
**Voorbeeld:**
```
users
------
id (PRIMARY KEY) | name | email
1 | Tim | tim@email.com
2 | Anna | anna@email.com
**Maak `.env.local` in je project root:**
```bash
# .env.local - NOOIT committen naar Git!
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx
```
**Waarom niet `email` als primary key?**
- Emails kunnen veranderen
- `id` is stabiel en snel
---
### Foreign Keys
**Wat:** Een kolom die verwijst naar de primary key van een andere tabel.
**Voorbeeld: Posts tabel**
```
posts
------
id | title | user_id (FOREIGN KEY → users.id)
1 | "Mijn blog" | 1
2 | "Hello world" | 1
3 | "Tips" | 2
```
**Wat zegt dit?**
- Post 1 en 2 zijn van user 1 (Tim)
- Post 3 is van user 2 (Anna)
---
### Relatie Types
**One-to-Many (1:N)** - Meest voorkomend!
```
Eén user → meerdere posts
Eén category → meerdere products
```
**One-to-One (1:1)** - Zeldzaam
```
Eén user → één profile
```
**Many-to-Many (N:N)** - Via tussentabel
```
Posts ↔ Tags (een post heeft meerdere tags, een tag heeft meerdere posts)
**Maak ook `.env.example` (WEL committen):**
```bash
# .env.example - template voor anderen
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
```
---
### One-to-Many Voorbeeld
### Supabase SDK Installeren
```
users posts
------ ------
id | name id | title | user_id
1 | Tim ←────────── 1 | "Blog 1" | 1
2 | Anna ←────┬───── 2 | "Blog 2" | 1
└───── 3 | "Tips" | 2
```bash
npm install @supabase/supabase-js
```
**Lees:** Tim heeft 2 posts, Anna heeft 1 post.
**Maak `src/lib/supabase.ts`:**
```typescript
import { createClient } from '@supabase/supabase-js'
---
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
### Many-to-Many met Tussentabel
```
posts post_tags tags
------ --------- ------
id | title post_id | tag_id id | name
1 | "React tips" 1 | 1 1 | "react"
2 | "CSS guide" 1 | 2 2 | "frontend"
2 | 2 3 | "css"
2 | 3
```
**Lees:**
- Post 1 heeft tags: react, frontend
- Post 2 heeft tags: frontend, css
---
### Normalisatie Basics
**Probleem: Data duplicatie**
```
orders (SLECHT)
------
id | customer_name | customer_email | product_name | price
1 | Tim | tim@email.com | Laptop | 999
2 | Tim | tim@email.com | Phone | 699
3 | Anna | anna@email.com | Laptop | 999
```
**Problemen:**
- Tim's email staat 2x (als hij verandert: 2 plekken updaten)
- "Laptop" en prijs staan 2x
---
### Genormaliseerde Versie
```
users products orders
------ -------- ------
id | name | email id | name | price id | user_id | product_id
1 | Tim | tim@... 1 | Laptop | 999 1 | 1 | 1
2 | Anna | anna@... 2 | Phone | 699 2 | 1 | 2
3 | 2 | 1
```
**Voordelen:**
- Elk gegeven staat 1x
- Update op 1 plek
- Minder opslagruimte
---
### NULL Values
**NULL = "geen waarde" (niet 0, niet "")**
```
users
------
id | name | phone
1 | Tim | 0612345678
2 | Anna | NULL ← Geen telefoon bekend
```
**Wanneer NULL toestaan?**
- Optionele velden (phone, description)
- Niet bij verplichte velden (name, email)
---
### Defaults
**Automatische waarde als je niks opgeeft:**
```
todos
------
id | title | completed | created_at
| | DEFAULT: false | DEFAULT: now()
```
Bij `INSERT INTO todos (title) VALUES ('Test')`:
```
id | title | completed | created_at
1 | Test | false | 2024-01-15 10:30:00
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
```
---
### Database Schema Tekenen
### CRUD Operaties
**Tools:** draw.io, Excalidraw, pen en papier
**Conventie:**
```
┌──────────────┐ ┌──────────────┐
│ users │ │ posts │
├──────────────┤ ├──────────────┤
│ id (PK) │───┐ │ id (PK) │
│ name │ │ │ title │
│ email │ └────→│ user_id (FK) │
│ created_at │ │ content │
└──────────────┘ │ created_at │
└──────────────┘
**C - Create (toevoegen):**
```typescript
const { data, error } = await supabase
.from('todos')
.insert({ title: 'Nieuwe taak' })
```
PK = Primary Key
FK = Foreign Key
Pijl = Relatie richting
**R - Read (ophalen):**
```typescript
const { data, error } = await supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false })
```
**U - Update (wijzigen):**
```typescript
const { data, error } = await supabase
.from('todos')
.update({ completed: true })
.eq('id', todoId)
```
**D - Delete (verwijderen):**
```typescript
const { error } = await supabase
.from('todos')
.delete()
.eq('id', todoId)
```
---
### Authenticatie met Auth UI
**Installeer auth packages:**
```bash
npm install @supabase/auth-ui-react @supabase/auth-ui-shared
```
**Login component:**
```tsx
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
import { supabase } from '@/lib/supabase'
export function LoginForm() {
return (
<Auth
supabaseClient={supabase}
appearance={{ theme: ThemeSupa }}
providers={[]}
magicLink={true}
/>
)
}
```
**Huidige user checken:**
```typescript
const { data: { user } } = await supabase.auth.getUser()
if (user) {
// User is ingelogd
console.log('Logged in as:', user.email)
} else {
// Redirect naar login
}
```
---
### Deployment naar Vercel
**Stap 1: Push naar GitHub**
```bash
git add .
git commit -m "Add Supabase integration"
git push
```
**Stap 2: Deploy op Vercel**
1. Ga naar [vercel.com](https://vercel.com)
2. "Add New Project"
3. Import je GitHub repo
4. **BELANGRIJK:** Voeg Environment Variables toe!
- `NEXT_PUBLIC_SUPABASE_URL`
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
5. Klik "Deploy"
**Stap 3: Supabase Redirect URLs**
1. Ga naar Supabase → Authentication → URL Configuration
2. Voeg toe bij "Redirect URLs":
- `https://jouw-app.vercel.app/**`
---
## Tools
- Pen en papier / Excalidraw / draw.io
- Supabase Table Editor (vooruitblik)
- Supabase
- Next.js
- OpenCode/WebStorm
- Vercel
- Git
---
## Lesopdracht (2 uur)
### Database Design Oefening
### Bouw een Todo App met Supabase
**Deel 1: Blog Database Ontwerpen (45 min)**
**Groepsdiscussie (15 min):**
Bespreek klassikaal de database schemas uit Les 7 - wie heeft welke structuur gekozen en waarom?
Ontwerp een database voor een blog met:
- Users (kunnen posts schrijven)
- Posts (hebben een auteur)
- Comments (op posts, door users)
**Deel 1: Supabase Setup (30 min)**
Voor elke tabel:
1. Teken de tabel met kolommen
2. Bepaal data types
3. Markeer Primary Keys
4. Markeer Foreign Keys
5. Teken de relaties
1. Maak Supabase account en project
2. Maak je tabellen via Table Editor (gebaseerd op Les 7 schema)
3. Kopieer credentials
4. Installeer `@supabase/supabase-js`
5. Maak `src/lib/supabase.ts`
6. Configureer `.env.local`
**Deel 2: Normalisatie Oefening (30 min)**
Test: `npm run dev` werkt zonder errors
Gegeven deze "slechte" tabel:
**Deel 2: CRUD Interface (1 uur)**
```
library
-------
book_title | author_name | author_email | borrower_name | borrowed_date
"1984" | "Orwell" | orwell@... | "Tim" | 2024-01-15
"1984" | "Orwell" | orwell@... | "Anna" | 2024-01-10
"Dune" | "Herbert" | herbert@... | "Tim" | 2024-01-12
```
Bouw UI voor todos:
1. Lijst van todos tonen
2. Form om nieuwe todo toe te voegen
3. Checkbox om todo af te vinken
4. Delete button per todo
Normaliseer naar aparte tabellen:
1. Welke entiteiten zie je?
2. Maak aparte tabellen
3. Voeg relaties toe
Gebruik AI hulp voor de components!
**Deel 3: Eindproject Schema (45 min)**
**Deel 3: Authenticatie (30 min)**
Ontwerp het database schema voor jouw eindproject:
1. Welke entiteiten heb je nodig?
2. Teken elke tabel met kolommen
3. Bepaal relaties
4. Documenteer je keuzes
1. Installeer auth packages
2. Maak login pagina met Auth UI
3. Toon alleen app voor ingelogde users
4. Test: login met magic link
### Deliverable
- Blog database schema (tekening)
- Genormaliseerde library database
- Eindproject database schema
- Werkende Todo app lokaal
- GitHub repository met code
- Screenshot van werkende app
---
## Huiswerk (2 uur)
### Verdieping en Voorbereiding
### Deploy naar Productie + Uitbreiden
**Deel 1: Eindproject Schema Uitwerken (1 uur)**
**Deel 1: Deployment (30 min)**
Werk je database schema volledig uit:
1. Push naar GitHub
2. Deploy naar Vercel
3. Configureer env vars in Vercel
4. Voeg Vercel URL toe aan Supabase Redirect URLs
5. Test: app werkt op productie URL!
1. **Per tabel:**
- Naam
- Alle kolommen met data types
- Primary key
- Foreign keys
- Defaults
- Nullable fields
**Deel 2: Features Uitbreiden (1 uur)**
2. **Documenteer:**
- Waarom deze structuur?
- Welke relaties?
- Eventuele alternatieve overwegingen
Voeg toe:
1. Filter buttons: Alle / Actief / Voltooid
2. Sorteer op datum (nieuwste eerst)
3. Loading state tijdens data ophalen
4. Error state bij problemen
5. Empty state: "Geen todos gevonden"
**Deel 2: Supabase Account (30 min)**
**Deel 3: Polish (30 min)**
Bereid je voor op volgende les:
1. Maak account op [supabase.com](https://supabase.com)
2. Verken de interface
3. Bekijk de Table Editor
**Deel 3: Reflectie (30 min)**
Beantwoord deze vragen (kort):
1. Wat is het verschil tussen primary en foreign key?
2. Waarom normaliseren we data?
3. Wanneer gebruik je one-to-many vs many-to-many?
4. Welke tabellen heeft jouw eindproject nodig?
1. Styling verbeteren met Tailwind
2. Responsive design (mobile friendly)
3. Kleine animaties (fade in/out)
### Deliverable
- Volledig uitgewerkt database schema voor eindproject
- Supabase account aangemaakt
- Reflectie vragen beantwoord
- Deployed app op Vercel (werkende URL!)
- Alle features werken in productie
- Screenshot van productie app
---
## Leerdoelen
Na deze les kan de student:
- Uitleggen wat een relationele database is
- Tabellen, kolommen en rijen beschrijven
- De juiste data types kiezen
- Primary keys en hun doel uitleggen
- Foreign keys en relaties begrijpen
- One-to-many en many-to-many relaties herkennen
- Het probleem van data duplicatie identificeren
- Een database normaliseren
- NULL values en defaults begrijpen
- Een database schema ontwerpen en tekenen
- Een Supabase project aanmaken en configureren
- Database schema implementeren via Table Editor
- Environment variables correct beheren
- De Supabase client installeren en configureren
- CRUD operaties uitvoeren met de Supabase SDK
- Authenticatie implementeren met Auth UI
- Deployen naar Vercel met environment variables
- Database principles uit Les 7 toepassen in de praktijk