# Les 8 — Docenttekst
## Supabase × Next.js + Auth

---

## Lesoverzicht

| Gegeven | Details |
|---------|---------|
| **Les** | 8 van 18 |
| **Onderwerp** | Supabase koppelen + Auth introductie |
| **Duur** | 3 uur (09:00 – 12:00) |
| **Voorbereiding** | Werkend QuickPoll project, Supabase project met polls/options tabellen, RLS ingesteld |
| **Benodigdheden** | Laptop, Cursor/VS Code, browser, Supabase account |

---

## Leerdoelen

Na deze les kunnen studenten:
1. De Supabase JavaScript client gebruiken in een Next.js project
2. Data ophalen via Supabase queries (select met relaties, eq, single)
3. Het Server Component + Client Component patroon toepassen
4. Uitleggen wat authenticatie vs autorisatie is
5. Supabase Auth functies gebruiken (signUp, signIn, signOut, getUser)
6. Een login/registratie flow bouwen in Next.js

---

## Lesvoorbereiding (voor docent)

Zorg dat je volgende zaken hebt voorbereiding:
- Een werkend Supabase project met `polls` en `options` tabellen (uit Les 7)
- RLS ingeschakeld op beide tabellen met policies voor SELECT (anon) en UPDATE (anon op options)
- De Next.js QuickPoll app uit Les 7 werkend op je machine
- De slides gereed voor uitleg authenticatie vs autorisatie
- Test je eigen Supabase credentials vooraf

---

## 09:00–09:10 | Welkom & Terugblik (10 min)

**Doel:** Studenten krijgen duidelijk wat we vandaag doen en waar we van vorige week waren.

### Wat we hebben gedaan in Les 7:
- ✅ Stemmen werkend gemaakt (votePoll functie, state update in poll detail page)
- ✅ Supabase introductie: account aangemaakt, project gemaakt
- ✅ Database: polls + options tabellen aangemaakt
- ✅ Foreign keys + CASCADE ingesteld
- ✅ RLS policies ingesteld (SELECT voor anon, UPDATE voor anon op options)
- ✅ Testdata ingevoerd via Table Editor

### Wat we NIET hebben afgemaakt in Les 7:
- ❌ Supabase is NIET aan het Next.js project gekoppeld
- ❌ Data wordt nog niet uit Supabase opgehaald
- ❌ Geen authenticatie

### Vandaag gaan we:
1. **DEEL 1 (65 min):** Supabase client installeren en opzetten → data uit database halen in Next.js
2. **DEEL 2a (30 min):** Uitleg over authenticatie, autorisatie en Supabase Auth features
3. **DEEL 2b (30 min):** Studenten bouwen auth zelf in hun project (signup, login, logout)

**Motivatie:** "Tot nu toe zijn je polls hardcoded in geheugen. Straks halen we echte data uit Supabase en kunnen people inloggen. Dat is een echt web app!"

---

## 09:10–10:15 | DEEL 1: Supabase Koppelen — Live Coding (65 min)

Dit deel volgt een stap-voor-stap aanpak met live coding. Alle studenten coderen mee.

### 09:10–09:15 | Installatie (5 min)

Open terminal in het QuickPoll project en run:

```bash
npm install @supabase/supabase-js
```

**Teacher Tip:** Controleer dat de installatie slaagt. Als students `npm ERR!` zien, laat ze eerst `npm clean-install` doen en daarna opnieuw proberen.

### 09:15–09:25 | Environment Variables (10 min)

Zorg dat alle studenten hun Supabase credentials veilig opslaan.

1. Open in Supabase Dashboard: **Settings** → **API**
2. Kopieer:
   - `Project URL` (eindigt op `.supabase.co`)
   - `anon` public key

3. Maak/open `.env.local` in je Next.js project root:

```env
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
```

**Belangrijk:**
- `.env.local` staat al in `.gitignore` (check even)
- Keys die beginnen met `NEXT_PUBLIC_` zijn zichtbaar in browser (maar anon keys zijn daarvoor bedoeld)
- **ALTIJD de dev server herstarten na wijzigen van `.env.local`** (Ctrl+C, dan `npm run dev`)

**Teacher Tip:** Dit is een veelvoorkomende fout. Zeg hardop: "Als jullie een leeg array zien in plaats van polls, check EERST of je dev server herstarten hebt!"

### 09:25–09:35 | Supabase Client aanmaken (10 min)

Maak `lib/supabase.ts`:

```typescript
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient(supabaseUrl, supabaseKey)
```

**Wat gebeurt hier:**
- We importeren `createClient` uit `@supabase/supabase-js`
- We halen URL en key uit environment variables
- We geven deze aan `createClient`
- We exporteren de client zodat we het overal kunnen gebruiken

**Teacher Tip:** TypeScript geeft mogelijk een warning over "null assertion (!)" — dat is OK. Dit zeggen we tegen TypeScript: "Deze values bestaan echt, vertrouw me."

### 09:35–09:45 | Database Types (10 min)

Maak `lib/types.ts` handmatig:

```typescript
export interface Poll {
  id: string
  created_at: string
  question: string
}

export interface Option {
  id: string
  poll_id: string
  text: string
  votes: number
}
```

**Waarom:** Dit helpt TypeScript begrijpen welke data we uit Supabase krijgen.

**Teacher Tip:** In een echt project zou je `npx supabase gen types typescript` gebruiken, maar dat kost extra setup. Voor deze les is handmatig OK.

### 09:45–10:00 | Async Data functies (15 min)

Update `lib/data.ts` — alle functies worden nu async en halen data uit Supabase:

```typescript
import { supabase } from './supabase'
import { Poll, Option } from './types'

export async function getPolls(): Promise<Poll[]> {
  const { data, error } = await supabase
    .from('polls')
    .select('*')
    .order('created_at', { ascending: false })

  if (error) {
    console.error('Error fetching polls:', error)
    return []
  }

  return data || []
}

export async function getOptions(pollId: string): Promise<Option[]> {
  const { data, error } = await supabase
    .from('options')
    .select('*')
    .eq('poll_id', pollId)
    .order('votes', { ascending: false })

  if (error) {
    console.error('Error fetching options:', error)
    return []
  }

  return data || []
}
```

**Wat betekent dit:**
- `.from('polls')` — welke tabel
- `.select('*')` — alle kolommen
- `.eq('poll_id', pollId)` — filter op poll_id
- `.order()` — sorteer op
- `await` — wacht op het resultaat van de database call
- Error handling — log en return empty array

**Teacher Tip:** Veel students maken hier fouten met async/await:
```typescript
// ❌ FOUT: promise niet awaited!
const data = supabase.from('polls').select('*')

// ✅ GOED:
const data = await supabase.from('polls').select('*')
```

### 10:00–10:10 | Homepage als Server Component (10 min)

Update `app/page.tsx` — dit wordt een Server Component:

```typescript
import { getPolls } from '@/lib/data'
import PollItem from '@/components/PollItem'

export default async function Home() {
  const polls = await getPolls()

  return (
    <div className="container mx-auto py-8">
      <h1 className="text-3xl font-bold mb-8">QuickPoll</h1>

      <div className="space-y-4">
        {polls.map((poll) => (
          <PollItem key={poll.id} poll={poll} />
        ))}
      </div>

      {polls.length === 0 && (
        <p className="text-gray-500">Geen polls beschikbaar.</p>
      )}
    </div>
  )
}
```

**Belangrijk:** Page.tsx is nu een **Server Component** — geen `'use client'` directive! We kunnen hier `async/await` rechtstreeks gebruiken.

**Teacher Tip:** Students vragen: "Maar hoe krijgen we de options?" — Goed punt! Die halen we in PollItem.

### 10:10–10:15 | PollItem Component (5 min)

Update `components/PollItem.tsx` — ook een Server Component:

```typescript
import { getOptions } from '@/lib/data'
import VoteForm from './VoteForm'
import { Poll } from '@/lib/types'

export default async function PollItem({ poll }: { poll: Poll }) {
  const options = await getOptions(poll.id)

  return (
    <div className="border rounded-lg p-4">
      <h2 className="text-lg font-semibold mb-3">{poll.question}</h2>

      <div className="space-y-2">
        {options.map((option) => (
          <VoteForm
            key={option.id}
            option={option}
            pollId={poll.id}
          />
        ))}
      </div>
    </div>
  )
}
```

**Waarom twee Server Components?**
- `page.tsx` ziet alleen alle polls (geen details)
- `PollItem` wordt per poll gerenderd en haalt zelf de options op (parallel!)
- Dit patroon is efficient en schaalbaar

**Teacher Tip:** Dit is het "Suspended Components" patroon van React 18 — Server Components voeren dit automatisch in parallel uit.

---

## 10:15–10:30 | PAUZE (15 min)

Goed moment om even weg te lopen. Tussendoor kun jij:
- Rondlopen en kijken wie nog errors heeft
- Checken of iedereen env vars juist ingesteld heeft
- Dev servers herstarten voor wie vergeten zijn
- Voorbereiding treffen voor DEEL 2

---

## 10:30–11:00 | DEEL 2a: Uitleg Auth (30 min)

Dit is uitleg — geen live coding nog. Zorg dat alle laptops dicht zijn, focus op slides en beamer.

### Authenticatie vs Autorisatie

**Authenticatie (Authentication):**
- "Wie ben je?" — identity verification
- Voorbeeld: Je logt in met email + password
- Supabase Auth zorgt hiervoor

**Autorisatie (Authorization):**
- "Wat mag je?" — permissions
- Voorbeeld: Je mag alleen je eigen polls aanpassen
- RLS (Row Level Security) in Supabase zorgt hiervoor

**Voorbeeld:**
- Auth: "Je email en password kloppen, je bent Alice."
- RLS: "Alice mag haar eigen polls zien en updaten, maar niet die van Bob."

### Supabase Auth Features

Demo op beamer:
1. Open Supabase Dashboard → **Authentication** → **Providers**
2. Toon dat **Email/Password** is ingeschakeld
3. Toon de instelling **"Confirm email"** (nu UIT voor dev)
4. Ga naar **Users** tab — hier zie je ingelogde users

**Supabase Auth ondersteunt:**
- Email/Password (wat we vandaag gebruiken)
- OAuth (Google, GitHub, etc.) — volgende week
- Magic Links (passwordless login)
- Session management (Supabase beheert cookies automatisch)

### @supabase/ssr vs @supabase/supabase-js

**@supabase/supabase-js:**
- Browser-side client
- Voor onClick handlers, forms, interactie

**@supabase/ssr:**
- Server-side client (SSR = Server-Side Rendering)
- Voor middleware, cookies, server actions
- Handelt sessions automatisch af

**Waarom twee?**
- Browser kan niet veilig geheimen beheren
- Server kan veilig cookies zetten
- Supabase SSR packages zorgen dat beide veilig werken

### Supabase Auth Functies

**signUp(email, password)** — nieuwe account aanmaken
```typescript
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password'
})
```

**signInWithPassword(email, password)** — inloggen
```typescript
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password'
})
```

**signOut()** — uitloggen
```typescript
await supabase.auth.signOut()
```

**getUser()** — huidge user ophalen
```typescript
const { data: { user } } = await supabase.auth.getUser()
// user is null als niemand ingelogd, anders is het een User object
```

### Server vs Browser Client

**Browser Client (createBrowserClient):**
- Voor 'use client' components
- Kan useState gebruiken
- Kan useRouter gebruiken
- Kan user events luisteren

**Server Client (createServerClient):**
- Voor server components en middleware
- Leest/schrijft cookies
- Kan getUser() veilig aanroepen
- Geen access tot browser APIs

### Middleware & Session Refresh

**Wat doet middleware?**
- Draait op elke request naar je app
- Refreshed de Supabase session
- Zorgt dat user state altijd up-to-date is

**Voorbeeld flow:**
1. User logt in op `/login` page
2. Cookie wordt gezet
3. Middleware ziet op volgende request: "Er is een session cookie!"
4. Middleware refreshed de session
5. App ziet dat user ingelogd is

### Handige links

Toon op slides:
- [Supabase Auth docs](https://supabase.com/docs/guides/auth/server-side/nextjs)
- [Next.js Server Components docs](https://nextjs.org/docs/getting-started/react-essentials)

---

## 11:00–11:30 | DEEL 2b: Zelf Doen — Auth Implementeren (30 min)

Nu gaan studenten zelf auth bouwen in hun project. Dit is niet meer live coding — docent loopt rond en helpt.

**Instructie voor studenten:**

Volg deze stappen. Docent loopt rond als je vragen hebt.

#### Stap 1: SSR Package Installeren (2 min)

```bash
npm install @supabase/ssr
```

#### Stap 2: Server Client (3 min)

Maak `lib/supabase-server.ts`:

```typescript
import { cookies } from 'next/headers'
import { createServerClient } from '@supabase/ssr'

export async function createClient() {
  const cookieStore = await cookies()

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return cookieStore.getAll()
        },
        setAll(cookiesToSet) {
          try {
            cookiesToSet.forEach(({ name, value, options }) =>
              cookieStore.set(name, value, options)
            )
          } catch {
            // Handle error
          }
        },
      },
    }
  )
}
```

**Wat is dit?** Dit is een helper zodat Supabase cookies kan beheren in Next.js. Copy-paste voor nu.

#### Stap 3: Browser Client (1 min)

Maak `lib/supabase-browser.ts`:

```typescript
import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )
}
```

**Wat is dit?** Dit gebruiken we in 'use client' components.

#### Stap 4: Middleware (5 min)

Maak `middleware.ts` in project root:

```typescript
import { type NextRequest, NextResponse } from 'next/server'
import { createServerClient } from '@supabase/ssr'

export async function middleware(request: NextRequest) {
  let supabaseResponse = NextResponse.next({
    request,
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        getAll() {
          return request.cookies.getAll()
        },
        setAll(cookiesToSet) {
          cookiesToSet.forEach(({ name, value, options }) => {
            supabaseResponse.cookies.set(name, value, options)
          })
        },
      },
    }
  )

  // Refresh user session
  await supabase.auth.getUser()

  return supabaseResponse
}

export const config = {
  matcher: [
    '/((?!_next/static|_next/image|favicon.ico|.*\\.svg|.*\\.png|.*\\.jpg|.*\\.jpeg).*)',
  ],
}
```

**Wat is dit?** Dit draait op elke request en refreshed de session. Copy-paste, don't worry.

#### Stap 5: Signup Page (5 min)

Maak `app/auth/signup/page.tsx`:

```typescript
'use client'

import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase-browser'

export default function SignUpPage() {
  const router = useRouter()
  const supabase = createClient()
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState('')
  const [loading, setLoading] = useState(false)

  const handleSignUp = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)
    setError('')

    const { error } = await supabase.auth.signUp({
      email,
      password,
    })

    if (error) {
      setError(error.message)
      setLoading(false)
    } else {
      router.push('/auth/login')
    }
  }

  return (
    <div className="flex items-center justify-center min-h-screen">
      <form onSubmit={handleSignUp} className="w-full max-w-md p-6 border rounded-lg">
        <h1 className="text-2xl font-bold mb-6">Sign Up</h1>

        {error && <div className="text-red-600 mb-4">{error}</div>}

        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="w-full px-4 py-2 border rounded mb-4"
          required
        />

        <input
          type="password"
          placeholder="Password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full px-4 py-2 border rounded mb-6"
          required
        />

        <button
          type="submit"
          disabled={loading}
          className="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
        >
          {loading ? 'Signing up...' : 'Sign Up'}
        </button>
      </form>
    </div>
  )
}
```

**Belangrijk:** `'use client'` directive bovenaan — dit is een interactive component!

#### Stap 6: Login Page (5 min)

Maak `app/auth/login/page.tsx`:

```typescript
'use client'

import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase-browser'
import Link from 'next/link'

export default function LoginPage() {
  const router = useRouter()
  const supabase = createClient()
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState('')
  const [loading, setLoading] = useState(false)

  const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)
    setError('')

    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    })

    if (error) {
      setError(error.message)
      setLoading(false)
    } else {
      router.push('/')
    }
  }

  return (
    <div className="flex items-center justify-center min-h-screen">
      <form onSubmit={handleLogin} className="w-full max-w-md p-6 border rounded-lg">
        <h1 className="text-2xl font-bold mb-6">Login</h1>

        {error && <div className="text-red-600 mb-4">{error}</div>}

        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          className="w-full px-4 py-2 border rounded mb-4"
          required
        />

        <input
          type="password"
          placeholder="Password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full px-4 py-2 border rounded mb-6"
          required
        />

        <button
          type="submit"
          disabled={loading}
          className="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
        >
          {loading ? 'Logging in...' : 'Login'}
        </button>

        <p className="mt-4 text-center text-sm">
          Nog geen account? <Link href="/auth/signup" className="text-blue-600 hover:underline">Sign up</Link>
        </p>
      </form>
    </div>
  )
}
```

#### Stap 7: Logout Button (3 min)

Maak `components/LogoutButton.tsx`:

```typescript
'use client'

import { useRouter } from 'next/navigation'
import { createClient } from '@/lib/supabase-browser'

export default function LogoutButton() {
  const router = useRouter()
  const supabase = createClient()

  const handleLogout = async () => {
    await supabase.auth.signOut()
    router.refresh()
  }

  return (
    <button
      onClick={handleLogout}
      className="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
    >
      Logout
    </button>
  )
}
```

**Belangrijk:** `router.refresh()` na logout zorgt dat page de nieuwe state ziet!

#### Stap 8: Navbar met Auth State (3 min)

Update `components/Navbar.tsx`:

```typescript
import { createClient } from '@/lib/supabase-server'
import Link from 'next/link'
import LogoutButton from './LogoutButton'

export default async function Navbar() {
  const supabase = await createClient()
  const { data: { user } } = await supabase.auth.getUser()

  return (
    <nav className="bg-gray-800 text-white p-4">
      <div className="container mx-auto flex justify-between items-center">
        <Link href="/" className="text-2xl font-bold">
          QuickPoll
        </Link>

        <div className="flex gap-4 items-center">
          {user ? (
            <>
              <span className="text-sm">{user.email}</span>
              <LogoutButton />
            </>
          ) : (
            <>
              <Link href="/auth/login" className="px-4 py-2 bg-blue-600 rounded hover:bg-blue-700">
                Login
              </Link>
              <Link href="/auth/signup" className="px-4 py-2 bg-green-600 rounded hover:bg-green-700">
                Sign Up
              </Link>
            </>
          )}
        </div>
      </div>
    </nav>
  )
}
```

**Logica:**
- Als `user` bestaat (ingelogd): toon email + Logout button
- Anders: toon Login + Sign Up buttons

#### Stap 9: Layout updaten (2 min)

Update `app/layout.tsx`:

```typescript
import type { Metadata } from 'next'
import Navbar from '@/components/Navbar'
import './globals.css'

export const metadata: Metadata = {
  title: 'QuickPoll',
  description: 'Vote on polls',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <Navbar />
        {children}
      </body>
    </html>
  )
}
```

Voeg gewoon `<Navbar />` toe.

**Teacher Tip: Studenten vastlopen?**
- Na 5-10 minuten vastzitten: toon de referentie code op beamer
- Zeg: "Dit is complex, copy-paste is OK. Focus op begrijpen, niet op typen."
- Help met debuggen (console.log, errors lezen)

---

## 11:30–11:45 | Vragen & Reflectie (15 min)

Hier zijn veelvoorkomende vragen:

### V: "Wat is het verschil tussen `createClient()` in server.ts en browser.ts?"
**A:**
- `server.ts`: kan cookies veilig beheren (server-side)
- `browser.ts`: kan UI events afhandelen (onClick, forms)
- Supabase kiest automatisch het juiste moment om te gebruiken

### V: "Waarom twee environment variables bovenaan?"
**A:**
- `NEXT_PUBLIC_SUPABASE_URL`: URL is public, iedereen ziet het
- `NEXT_PUBLIC_SUPABASE_ANON_KEY`: anon key is public (maar kan geen private data lezen)
- Private keys (service role) zetten we NIET in .env.local, die gaan in server.ts als geheim

### V: "Mijn login werkt niet, ik krijg error"
**A:** Check:
1. Klopt je email/password echt?
2. Is je account in Supabase Dashboard → Authentication → Users?
3. Is Email provider ingeschakeld?
4. Zit "Confirm email" uit? (check dashboard)

### V: "Logout werkt niet, user staat nog ingelogd"
**A:** Vergeten `router.refresh()` na `signOut()`?

### V: "Middleware error: 'createServerClient is not defined'"
**A:** Check je import: moet `import { createServerClient } from '@supabase/ssr'` zijn

### V: "Kan ik als anonieme user stemmen?"
**A:** Ja! RLS policy staat op `FOR SELECT, UPDATE TO authenticated` — maar je Navbar toont Login/Signup want je bent nog niet ingelogd. Dat is OK. Volgende les doen we RLS policies correct.

---

## 11:45–12:00 | Huiswerk & Afsluiting (15 min)

### Huiswerk (voor Les 9):

**Verplicht:**
1. **/create pagina bouwen** — studenten voegen nieuwe polls toe via een form
   - Maak `app/create/page.tsx` (Server Component met form als Client Component)
   - Form met: vraag + array van 2-3 opties
   - `supabase.from('polls').insert()` en `supabase.from('options').insert()`
   - Zorg dat je eigen `user_id` meestuurt

2. **RLS INSERT policy** — alleen authenticated users mogen polls toevoegen
   - Supabase Dashboard → Authentication → Policies
   - Voeg policy toe: `INSERT` voor authenticated users
   - `user_id = auth.uid()`

3. **Optional extras (challenge):**
   - Toon poll creator in PollItem
   - Google OAuth inschakelen (zie Supabase docs)
   - Edit/Delete buttons (alleen voor je eigen polls)

### Afsluitingsboodschap:

"Gefeliciteerd! Vandaag hebben jullie:
- Supabase gekoppeld aan Next.js
- Real data uit een database geladen
- Login/logout gebouwd
- Server & browser clients begrepen

Volgende week voegen we RLS policies toe zodat iedereen alleen zijn eigen polls kan aanpassen. Dat is waar authenticatie écht nuttig wordt!"

---

## Veelvoorkomende Problemen

| Probleem | Oorzaak | Oplossing |
|----------|---------|-----------|
| `Error: Cannot find module '@supabase/supabase-js'` | Package niet geïnstalleerd | `npm install @supabase/supabase-js` en dev server herstarten |
| Supabase returns leeg array | .env.local niet juist of dev server niet herstarten | Check .env.local, restart dev server (Ctrl+C + `npm run dev`) |
| TypeScript complains over `null assertion (!)` | Normale TS warning | Dit is OK, we vertellen TS dat env vars bestaan |
| `'use client' vergeten in signup/login page` | Component is interactief maar geen directive | Voeg `'use client'` bovenaan toe |
| Login page blank/geen content | Conflict met server components | Zorg ALL pages onder /auth zijn `'use client'` |
| Logout werkt niet, user nog ingelogd | `router.refresh()` niet aangeroepen | Voeg `await router.refresh()` toe na `signOut()` |
| Middleware error: "wrong params" | Onjuiste URL of key in middleware | Copy-paste van .env.local, check Format |
| "Invalid token" bij Supabase calls | Token verlopen of anon key fout | Restart dev server, check API credentials |
| User niet in Authentication → Users | Signup failed, geen account aangemaakt | Check browser console op errors, probeer opnieuw met ander email |
| `router.refresh()` werkt niet in component | Router niet geïmporteerd | `import { useRouter } from 'next/navigation'` (niet 'next/router'!) |
| Cors/network error | Supabase URL fout | Check dat URL eindigt op `.supabase.co` en https:// bevat |
| Password te kort / validation error | Supabase vereist min 6 chars | Zeg studenten: "Test met password123" |

---

## Didactische Tips

- **Pair Programming:** Zet snelle studenten samen met tragere — kennis spreidt zich uit
- **Show & Tell:** Toon je eigen werkend QuickPoll op beamer — studenten zien het doel
- **Error-driven Learning:** Zeg niet meteen het antwoord, vraag: "Wat zegt de error?"
- **Debug together:** Als iemand vastlopen, use browser console.log + devtools
- **Save time** — als >3 students dezelfde error hebben, stop even en toon op beamer
- **Celebrate wins** — als iemand eerste Signup working heeft, geef thumbs up!

---

## Referentiematerialen voor Studenten

- [Supabase Auth docs](https://supabase.com/docs/guides/auth/server-side/nextjs)
- [Next.js Server Components](https://nextjs.org/docs/getting-started/react-essentials)
- [Environment Variables in Next.js](https://nextjs.org/docs/basic-features/environment-variables)
- Alle code snippets uit deze docenttekst

---

**Einde docenttekst Les 8**
