Files
novi-lessons/Les08-Supabase-Auth/Les08-Docenttekst.md
2026-03-31 16:06:18 +02:00

26 KiB
Raw Blame History

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:0009: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:1010: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:1009:15 | Installatie (5 min)

Open terminal in het QuickPoll project en run:

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:1509:25 | Environment Variables (10 min)

Zorg dat alle studenten hun Supabase credentials veilig opslaan.

  1. Open in Supabase Dashboard: SettingsAPI

  2. Kopieer:

    • Project URL (eindigt op .supabase.co)
    • anon public key
  3. Maak/open .env.local in je Next.js project root:

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:2509:35 | Supabase Client aanmaken (10 min)

Maak lib/supabase.ts:

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:3509:45 | Database Types (10 min)

Maak lib/types.ts handmatig:

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:4510:00 | Async Data functies (15 min)

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

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:

// ❌ FOUT: promise niet awaited!
const data = supabase.from('polls').select('*')

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

10:0010:10 | Homepage als Server Component (10 min)

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

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:1010:15 | PollItem Component (5 min)

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

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:1510: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:3011: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 → AuthenticationProviders
  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

const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password'
})

signInWithPassword(email, password) — inloggen

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

signOut() — uitloggen

await supabase.auth.signOut()

getUser() — huidge user ophalen

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

Toon op slides:


11:0011: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)

npm install @supabase/ssr

Stap 2: Server Client (3 min)

Maak lib/supabase-server.ts:

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:

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:

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:

'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:

'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:

'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:

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:

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:3011: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:4512: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


Einde docenttekst Les 8