# Les 13 — Externe APIs + Cursor agents + Vercel deploy ## Slide Overzicht (Klas A — 3 uur fysiek, demo-driven) **Lesvorm:** Tim demonstreert klassikaal. Studenten kijken. Zelf bouwen = huiswerk. **Demo-app:** Nieuwe kleine Pokédex (PokéAPI, geen key, visueel) **Vervolg op:** Les 12 — Tool Calling **Aansluit op:** Les 14 — Agents --- ## Slide 1: Title ### Les 13 — Externe APIs + Cursor + Vercel **Visual:** - Background: CREAM - "Les 13" in BLUE - "Externe APIs + Cursor + Vercel" in BLACK - Subtitle: "Van localhost naar productie — met Cursor agents en preview deploys" --- ## Slide 2: Terugblik ### Waar staan we? **Vorige lessen:** - Les 11: AI SDK basics - Les 12: Tool Calling - Les 13: Agents **Tot nu toe:** alles draaide op `localhost:3000`. Geen wereld eromheen. **Vandaag:** - Externe APIs aanroepen (geen LLM nodig, gewoon data ophalen) - App naar productie met Vercel - Feature branches met Cursor agents (Composer + Background) - GitHub Actions CI (lint + build per PR) **Visual:** localhost icoon → cloud icoon met branches eruit. --- ## Slide 3: Planning ### Vandaag — 180 minuten | Onderwerp | Duur | |-----------|------| | Welkom + Terugblik | 10 min | | Theorie: Externe APIs in Next.js | 15 min | | Theorie: Cursor agents — Composer vs Background | 15 min | | Theorie: Vercel preview deploys + GH Actions | 15 min | | **Live Demo 1** — Pokédex bouwen met Composer | 30 min | | **Live Demo 2** — Deploy naar Vercel productie | 15 min | | **Pauze** | 15 min | | **Live Demo 3** — Feature branch met Background Agent | 25 min | | **Live Demo 4** — GitHub Actions CI | 15 min | | Composer vs Background — wanneer welke? | 10 min | | Lesopdracht + Huiswerk | 10 min | | Vragen + Afsluiting | 5 min | **Format:** Demo-driven, Cursor en browser side-by-side. --- ## Slide 4: Wat is een externe API? ### Data van buiten, in jouw app **Het idee:** - Andere mensen / bedrijven hebben data of functionaliteit - Ze stellen die beschikbaar via HTTP endpoints (REST) - Jij doet een `fetch()` en krijgt JSON terug **Voorbeelden:** - PokéAPI — Pokemon data (geen key) - Open-Meteo — weer (geen key) - GitHub API — repos, users, issues - Tavily — web search (key nodig) - Stripe — betalingen (key + secret) **API keys** horen in environment variables — nooit in je code committen. **Visual:** Diagram: Next.js → fetch → externe API → JSON terug. --- ## Slide 5: Drie manieren om een API te fetchen in Next.js ### Static, Server (dynamic), Client — wanneer welke? | | Wanneer gerenderd | Data versheid | Externe API key veilig? | Wanneer kiezen | |---|---|---|---|---| | **Static** (build-time) | Tijdens `next build` | Bij elke build | Ja, server | Data verandert zelden (blog, marketing, lijst) | | **Server / Dynamic** (request-time) | Bij elke request | Altijd vers | Ja, server | Data per user/request anders, of telkens vers nodig | | **ISR** (revalidate) | Eerste keer + na X seconden | Periodiek vers | Ja, server | Sweet spot: snel + redelijk vers (Pokédex!) | | **Client** (`useEffect`) | In de browser na hydration | Live | **Nee** — sleutel staat in browser | User-interactie, live updates, geen geheim | **Code-verschil = één optie in `fetch`:** ```typescript // 1. STATIC — gecached forever (default in Next.js!) const res = await fetch(URL); // 2. ISR — cache met revalidate const res = await fetch(URL, { next: { revalidate: 3600 } }); // 3. DYNAMIC — altijd vers const res = await fetch(URL, { cache: "no-store" }); ``` **Vandaag gebruiken we:** Static voor de Pokédex-lijst, ISR voor detail-pages. --- ## Slide 6: Cursor — twee soorten agents ### Composer vs Background | | Composer | Background Agent | |---|----------|------------------| | Waar draait | Lokaal in je editor | Cursor cloud | | Mode | Synchroon — jij wacht | Async — runt zonder jou | | Output | Diffs in editor | Branch + PR op GitHub | | Goed voor | Pair programming, snelle changes | Welomschreven feature, tijdens andere taak | | Aanroep | `Cmd+I` (Mac) / `Ctrl+I` | `Cmd+Shift+P` → Background Agent | | Repo nodig | Lokaal | Verbonden met Cursor cloud | **Beide kanten:** - Composer: jij stuurt, snel feedback, niet schaalbaar voor lange taken - Background: schaalbaar (5 agents tegelijk), maar minder controle in flight **Visual:** Twee tracks naast elkaar — synchroon (jij + composer) vs asynchroon (background agent + jij elders). --- ## Slide 7: Vercel — productie, preview, environment variables ### Wat verandert er per omgeving? **Drie omgevingen, drie scopes:** | Scope | Wordt gedeployed bij | URL | Wie ziet het | |-------|---------------------|-----|--------------| | **Production** | Push naar `main` | `je-app.vercel.app` | Iedereen | | **Preview** | Push naar elke andere branch | `je-app-git-{branch}-{user}.vercel.app` | Iedereen met link (delen via PR) | | **Development** | Lokaal `pnpm dev` | `localhost:3000` | Alleen jij | **Environment variables per scope:** In Vercel → Project → Settings → Environment Variables zet je per var **drie keer** een waarde: | Var | Production | Preview | Development | |-----|------------|---------|-------------| | `DATABASE_URL` | prod database | staging database | lokale database | | `OPENAI_API_KEY` | echte key | test-key (lage limit) | jouw persoonlijke key | | `STRIPE_KEY` | `sk_live_...` | `sk_test_...` | `sk_test_...` | | `NEXT_PUBLIC_APP_URL` | je-app.com | preview-url | localhost:3000 | **Waarom is dit belangrijk?** - Preview branches mogen NIET stagecoach naar je productie-database schrijven - Test API-keys voorkomen kosten/quota-problemen bij experiments - Eén branch = één geïsoleerde wereld om in te testen **Cursor Background Agent + Vercel preview = killer combo:** Agent opent PR → preview URL automatisch in PR-comment → reviewer test live in browser, geen lokale setup nodig. --- ## Slide 8: GitHub Actions CI ### Lint + build per PR **Waarom:** - Voor merge weet je: code lint clean én build slaagt - Voorkomt "werkte op mijn laptop" - Background Agent doet PR → CI draait automatisch → groene check of feedback **Minimaal `.github/workflows/ci.yml`:** ```yaml name: CI on: pull_request: branches: [main] push: branches: [main] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v3 with: { version: 9 } - uses: actions/setup-node@v4 with: { node-version: 20, cache: pnpm } - run: pnpm install - run: pnpm lint - run: pnpm build ``` **Tijd per CI-run:** ~1-2 min op een kleine Next.js app. --- ## Slide 9: Wat we vandaag bouwen ### Pokédex — Next.js + PokéAPI **Doel:** Een mini-Pokédex om de hele pipeline te oefenen. **Stack:** - Next.js 16 + TypeScript + Tailwind + Shadcn - PokéAPI (geen key, gratis, geen rate-limit bij normaal gebruik) - Cursor (Composer + Background Agent) - GitHub (repo + Actions) - Vercel (productie + preview deploys) **Features die we bouwen vandaag:** | Feature | Met | Branch | |---------|-----|--------| | Pokédex list (eerste 151) | Composer | main | | Detail page per Pokemon | Composer | main | | Search met filter | Background Agent | feature/search | | Type-filter chips | Background Agent | feature/type-filter | **Visual:** Folder-tree + GitHub branch-diagram + Vercel URLs. --- ## Slide 10: LIVE DEMO 1 — Pokédex met Composer + rendering modes ### ~30 min **Wat ik laat zien:** 1. `pnpm create next-app pokedex --typescript --tailwind --app` 2. Open in Cursor 3. **Composer (`Cmd+I`)**: "Bouw een lijst-pagina met de eerste 151 Pokémon van PokéAPI als statische pagina. Gebruik Tailwind cards. De fetch moet static zijn — geen revalidate." 4. Composer maakt: `app/page.tsx` met `fetch(URL)` (static) + map + cards 5. `pnpm dev` — werkt 6. **Composer**: "Voeg detail-pagina toe op /pokemon/[name] met sprite + types + stats. Gebruik ISR met revalidate van 3600 seconden." 7. Composer maakt: `app/pokemon/[name]/page.tsx` met `{ next: { revalidate: 3600 } }` 8. **Bewijs in de browser:** open detail-pagina, refresh — direct geladen (statisch). Verander URL → andere Pokémon → ook snel (ISR cache). 9. **Show de Network tab:** geen calls naar PokéAPI vanuit browser, alles server. 10. **Bonus:** maak één pagina met `cache: "no-store"` (dynamic) zodat studenten alle drie de modi gezien hebben. **Belangrijk om uit te leggen:** - Composer is een TAB in Cursor — niet hetzelfde als Chat - Multi-file edits zichtbaar als diffs — je accepteert per file - Terminal commands vragen toestemming - De rendering-mode is één optie in `fetch()` — verder verandert je code niet **Visual:** Cursor screenshot met Composer panel. --- ## Slide 11: LIVE DEMO 2 — Deploy naar Vercel + Environment variables ### ~20 min **Wat ik laat zien:** 1. **GitHub:** new repo `pokedex` — public 2. **Lokaal:** ```bash git init git add . && git commit -m "Initial Pokédex" git branch -M main git remote add origin https://github.com/USER/pokedex.git git push -u origin main ``` 3. **Vercel:** Add New → Import Git Repository → kies `pokedex` → Deploy 4. Wachten 60-90s op build → Productie URL — werkt! 5. **Environment Variables demo:** voeg een var toe in `app/page.tsx`: ```tsx
Omgeving: {process.env.NEXT_PUBLIC_APP_ENV}
``` 6. **Vercel → Settings → Environment Variables:** - `NEXT_PUBLIC_APP_ENV` = `production` → vinkje bij Production - `NEXT_PUBLIC_APP_ENV` = `preview` → vinkje bij Preview - Lokaal: `.env.local` → `NEXT_PUBLIC_APP_ENV=development` 7. Redeploy → productie URL toont "production" 8. Maak feature branch `chore/banner`, push → preview URL toont "preview" 9. Lokaal `pnpm dev` → toont "development" **Wat studenten moeten zien:** - Push to main = productie deploy - Push to andere branch = preview deploy (eigen URL) - Per omgeving andere env-waarden - Voor secrets: vinkje **alleen** bij Production (preview-key gebruiken in preview!) --- ## Slide 12: Pauze ### 15 minuten --- ## Slide 13: LIVE DEMO 3 — Feature branch + Background Agent ### ~25 min **Wat ik laat zien:** 1. **Cursor:** `Cmd+Shift+P` → "Open Background Agent" 2. **Verbind** repo aan Cursor cloud (eenmalig — accepteren in browser) 3. **Prompt aan Background Agent:** > "Maak een nieuwe branch `feature/search`. Voeg een zoekbalk toe bovenaan de homepage. Wanneer iemand typt, filter de Pokémon-lijst op naam. Gebruik client-side state. Push de branch + open een PR met titel 'Add search bar'." 4. **Background Agent gaat aan de slag** — Tim laat zien hoe je 'm volgt in het Cursor panel 5. **~3-5 minuten later:** PR verschijnt op GitHub 6. **Vercel** comment in PR: preview URL → klik → live preview! 7. **Demo:** zoeken werkt op preview URL 8. **Code review** in GitHub UI: wat heeft de agent gedaan? 9. **Merge** → main → productie deploy **Tweede prompt — parallel:** > "Maak branch `feature/type-filter`. Voeg chips toe bij elk type (fire, water, ...) om te filteren." Beide branches kunnen tegelijk draaien. **Visual:** Cursor Background Agent panel + GitHub PR + Vercel preview URL. --- ## Slide 14: LIVE DEMO 4 — GitHub Actions CI ### ~15 min **Wat ik laat zien:** 1. **Vraag aan Composer:** > "Voeg een GitHub Actions workflow toe: lint + build moeten slagen op PR's naar main." 2. Composer maakt `.github/workflows/ci.yml` 3. Push naar nieuwe branch `chore/add-ci` 4. Open PR → GitHub Actions tab → workflow draait 5. **Demo failing CI:** introduceer typo (`