# Les 11 — Lesopdracht ## Bouw je eigen AI + Supabase app **Vak:** AI-Assisted Development **Opleiding:** NOVI Hogeschool Utrecht **Wanneer:** Thuis, vóór volgende les **Inleveren:** GitHub URL + screenshot van werkende chat --- ## Doel Bouw een complete versie van wat Tim live demonstreerde — **met je eigen thema**. Tim bouwde Polderfest 2027. Jij bouwt iets anders. Je oefent end-to-end: - Next.js project from scratch - Nieuwe Supabase aanmaken + koppelen - Eigen seed-script schrijven (mag AI bij helpen) - Chat-route + chat-UI met `streamText` + `useChat` - 3 vragen kunnen stellen die alleen via jouw data beantwoord kunnen worden > **Belangrijk:** dit is **niet** dezelfde Polderfest-demo namaken. Je kiest een eigen thema. Anders leer je vooral kopiëren. --- ## Vereisten ### Jouw thema moet: - [ ] **Volledig fictief zijn** — geen Spotify, geen restaurants in Amsterdam, geen Wikipedia-data - [ ] Minstens 5 velden hebben (waarvan 1 categorisch, 1 numeriek, 1 tekstrijk) - [ ] Minstens **100 records** bevatten - [ ] Vragen oproepen die je écht niet aan ChatGPT kunt stellen zonder jouw data ### Verboden thema's (te bekend voor LLMs): - Restaurants in een echte stad - Films / muziek / boeken die echt bestaan - Sport-statistieken uit de echte wereld - Klimaat / financiële data uit publieke bronnen ### Geschikte thema-richtingen (kies of bedenk eigen): - Fictief restaurant-aggregator in een verzonnen stad (bv. "Polderstad") - Galactische bestuurders archief (sci-fi) - Verzonnen scriptie-archief van NOVI (1000 nep-titels) - Fictieve museumcollectie met verzonnen kunstenaars - Fictief NPO-programma overzicht voor 2030 - Verzonnen mysteries / cryptid-sightings database NL - D&D campagne-NPCs voor een fictieve wereld - Fictieve nederlands ondernemers ecosysteem --- ## Stappenplan ### Stap 1 — Thema kiezen + schema ontwerpen (30 min) - Bedenk thema - Schrijf op papier (of in Notion): welke velden? Welke vragen wil je kunnen stellen? - Vertaal naar Supabase SQL schema (zie `schema.sql` van Polderfest als voorbeeld) ### Stap 2 — Next.js project + Supabase (15 min) ```bash npx create-next-app@latest mijn-thema \ --typescript --tailwind --app --eslint --no-src-dir --turbopack cd mijn-thema npm i @supabase/supabase-js ai @ai-sdk/openai zod npm i tsx dotenv --save-dev ``` - Maak nieuwe Supabase project aan - Run je schema in SQL Editor - Vul `.env.local` met URL + keys + OpenAI key ### Stap 3 — Seed script (45 min) Open `seed-polderfest.ts` (zie bijlage) als referentie. Je hebt twee opties: **Optie A — Met de hand:** - Kopieer de structuur - Vervang Polderfest-bouwstenen (genres, stages, cities…) door jouw thema-bouwstenen - Pas de `generateBand` functie aan naar `generateItem` voor jouw thema **Optie B — Met AI hulp (aanbevolen):** - Open OpenCode / Cursor met `seed-polderfest.ts` als context - Vraag: "Pas dit seed-script aan voor [mijn thema]. Schema is [paste schema]. Genereer 100+ records." - Review wat AI maakt — vragen om aanpassingen waar nodig Run je seed: ```bash npx tsx scripts/seed-mijn-thema.ts ``` Verifieer in Supabase Table Editor: 100+ records. ### Stap 4 — Chat route (20 min) `app/api/chat/route.ts` — gebruik Polderfest-versie als template: ```typescript import { streamText } from "ai"; import { openai } from "@ai-sdk/openai"; import { createClient } from "@supabase/supabase-js"; const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, ); export async function POST(req: Request) { const { messages } = await req.json(); const { data: items } = await supabase.from("items").select("*"); const context = items!.map((i) => `- ${i.name} (${i.category}, ${i.rating})` ).join("\n"); const system = `Je bent een assistent voor [thema-naam]. Hier is alle data: ${context} Beantwoord vragen op basis van bovenstaande data. Verzin niets. Antwoord in het Nederlands.`; const result = streamText({ model: openai("gpt-4o-mini"), system, messages, }); return result.toDataStreamResponse(); } ``` ### Stap 5 — Chat UI (15 min) `app/chat/page.tsx` — kopieer Polderfest UI, pas titel + placeholder aan. ### Stap 6 — Testen + 3 vragen (15 min) Browse naar `/chat`. Stel deze 3 vragen aan jouw AI: 1. Een **filter-vraag** ("Welke X hebben Y?") 2. Een **aggregatie-vraag** ("Hoeveel X zijn er in totaal?" / "Wie heeft de hoogste Z?") 3. Een **samenvatting-vraag** ("Vat de Z-categorie samen in 3 zinnen") Screenshots van werkende antwoorden bewaren — die heb je nodig. --- ## Inleveren (vóór volgende les) 1. **GitHub repo** met je code 2. **3 screenshots** van werkende chat-antwoorden in je README 3. **Eén alinea** onder de screenshots: waarom kan een gewone LLM deze vragen niet beantwoorden zonder jouw data? --- ## Veelvoorkomende problemen | Probleem | Oplossing | |----------|-----------| | `OPENAI_API_KEY is not defined` | Dev server herstarten na `.env.local` aanpassen | | Supabase insert: `permission denied` | Gebruik `SUPABASE_SERVICE_ROLE_KEY` in seed-script (geen anon) | | AI verzint dingen | System prompt verstevigen: "Verzin niets. Gebruik alleen onze data." | | AI antwoordt in Engels | Voeg toe aan prompt: "Antwoord in het Nederlands." | | Chat hangt / streamt niet | API endpoint moet `result.toDataStreamResponse()` returnen | | `tsx command not found` | `npm i tsx --save-dev`, run met `npx tsx ...` | --- ## Tijd-indicatie | Stap | Tijd | |------|------| | Thema + schema bedenken | 30 min | | Project + Supabase setup | 15 min | | Seed script (met AI hulp) | 45 min | | Chat route + UI | 35 min | | Testen + screenshots | 15 min | | **Totaal** | **~2,5 uur** | Loop je vast? Vraag in Brightspace of plan korte 1-op-1 met Tim. --- ## Tips - **Begin klein.** 100 records is genoeg. 500 als je extra wil. - **AI laten helpen bij seed.** Schaal je productiviteit 10×. - **System prompt is je hefboom.** Goede prompt = goede antwoorden. Slechte prompt = AI verzint. - **Test met simpele vraag eerst** ("Hoeveel records zijn er?"). Dan opbouwen. - **Token cost in de gaten houden** — onze hele les kostte <2 cent. Jouw test ook. Succes!