fix: all
This commit is contained in:
@@ -71,51 +71,41 @@
|
||||
|
||||
**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)
|
||||
- GitHub API — repos, users, issues
|
||||
- Open-Meteo — weer (geen key)
|
||||
|
||||
**In Next.js — twee plekken om te fetchen:**
|
||||
|
||||
| Server-side (RSC / API route) | Client-side (`useEffect`) |
|
||||
|-------------------------------|---------------------------|
|
||||
| Snel, key veilig, gecached | Nodig voor user-interactie |
|
||||
| Voor initiële data | Voor live updates |
|
||||
**API keys** horen in environment variables — nooit in je code committen.
|
||||
|
||||
**Visual:** Diagram: Next.js → fetch → externe API → JSON terug.
|
||||
|
||||
---
|
||||
|
||||
## Slide 5: Externe API in Next.js — code
|
||||
### Server Component met fetch
|
||||
## 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
|
||||
// app/pokemon/[name]/page.tsx
|
||||
async function getPokemon(name: string) {
|
||||
const res = await fetch(
|
||||
`https://pokeapi.co/api/v2/pokemon/${name}`,
|
||||
{ next: { revalidate: 3600 } } // cache 1 uur
|
||||
);
|
||||
if (!res.ok) throw new Error("Pokemon niet gevonden");
|
||||
return res.json();
|
||||
}
|
||||
// 1. STATIC — gecached forever (default in Next.js!)
|
||||
const res = await fetch(URL);
|
||||
|
||||
export default async function PokemonPage({ params }) {
|
||||
const pokemon = await getPokemon(params.name);
|
||||
return (
|
||||
<div>
|
||||
<h1>{pokemon.name}</h1>
|
||||
<img src={pokemon.sprites.front_default} alt="" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 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" });
|
||||
```
|
||||
|
||||
**Drie dingen om te weten:**
|
||||
1. **`async`** Server Component — fetch direct in component
|
||||
2. **`next: { revalidate }`** — Next.js cache opties
|
||||
3. **Error handling** — `!res.ok` netjes afvangen
|
||||
**Vandaag gebruiken we:** Static voor de Pokédex-lijst, ISR voor detail-pages.
|
||||
|
||||
---
|
||||
|
||||
@@ -139,27 +129,35 @@ export default async function PokemonPage({ params }) {
|
||||
|
||||
---
|
||||
|
||||
## Slide 7: Vercel preview deploys
|
||||
### Een URL per branch — automatisch
|
||||
## Slide 7: Vercel — productie, preview, environment variables
|
||||
### Wat verandert er per omgeving?
|
||||
|
||||
**Hoe het werkt:**
|
||||
1. Connect je GitHub repo aan Vercel (eenmalig)
|
||||
2. Push naar `main` → **productie** deploy op `je-app.vercel.app`
|
||||
3. Push naar elke andere branch → **preview** deploy op `je-app-git-{branch}-{user}.vercel.app`
|
||||
4. PR krijgt automatisch een comment met preview URL
|
||||
**Drie omgevingen, drie scopes:**
|
||||
|
||||
**Wat krijg je:**
|
||||
- Stakeholders zien een nieuwe feature voor de merge
|
||||
- Cursor Background Agent maakt een PR → preview URL → klikbaar
|
||||
- Reviewer test live in browser, niet alleen code-diff
|
||||
| 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 scopes:**
|
||||
**Environment variables per scope:**
|
||||
|
||||
| Scope | Wanneer | Voorbeeld var |
|
||||
|-------|---------|---------------|
|
||||
| **Production** | Deploys vanaf `main` | Echte API key |
|
||||
| **Preview** | Alle andere branches | Test API key |
|
||||
| **Development** | Lokaal (`.env.local`) | Dev API key |
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
@@ -224,31 +222,33 @@ jobs:
|
||||
|
||||
---
|
||||
|
||||
## Slide 10: LIVE DEMO 1 — Pokédex met Composer
|
||||
## 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, gebruik Tailwind cards"
|
||||
4. Composer maakt: `app/page.tsx` met fetch + map + cards
|
||||
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"
|
||||
7. Composer maakt: `app/pokemon/[name]/page.tsx`
|
||||
8. Cards op homepage linken naar detail-pages
|
||||
9. Klein bugje fix-en in chat: "de cards zijn te smal"
|
||||
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 productie
|
||||
### ~15 min
|
||||
## Slide 11: LIVE DEMO 2 — Deploy naar Vercel + Environment variables
|
||||
### ~20 min
|
||||
|
||||
**Wat ik laat zien:**
|
||||
1. **GitHub:** new repo `pokedex` — public
|
||||
@@ -261,15 +261,24 @@ jobs:
|
||||
git push -u origin main
|
||||
```
|
||||
3. **Vercel:** Add New → Import Git Repository → kies `pokedex` → Deploy
|
||||
4. Wachten 60-90s op build
|
||||
5. Open productie URL — werkt!
|
||||
6. **Wijziging:** title aanpassen, push naar main → 2e deploy, ~30s
|
||||
4. Wachten 60-90s op build → Productie URL — werkt!
|
||||
5. **Environment Variables demo:** voeg een var toe in `app/page.tsx`:
|
||||
```tsx
|
||||
<p>Omgeving: {process.env.NEXT_PUBLIC_APP_ENV}</p>
|
||||
```
|
||||
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:**
|
||||
- One-time setup (Vercel verbinden)
|
||||
- Daarna: push to main = deploy
|
||||
- Build logs in Vercel dashboard
|
||||
- Custom domain mogelijk (skipping vandaag)
|
||||
- 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!)
|
||||
|
||||
---
|
||||
|
||||
@@ -375,21 +384,18 @@ Beide branches kunnen tegelijk draaien.
|
||||
|
||||
**Vandaag gezien:**
|
||||
- Externe API integratie in Server Components
|
||||
- Static / ISR / Dynamic / Client — vier rendering modes
|
||||
- Cursor Composer voor synchrone changes
|
||||
- Cursor Background Agent voor async features
|
||||
- Vercel productie + preview per branch
|
||||
- Environment variables per omgeving (production / preview / development)
|
||||
- GitHub Actions CI (lint + build)
|
||||
- Wanneer welke Cursor-modus
|
||||
|
||||
**Volgende les (Les 16): MCP — Model Context Protocol**
|
||||
- Wat is MCP (Model Context Protocol)
|
||||
- Bestaande MCP servers gebruiken in Cursor/Claude Desktop
|
||||
- Eigen MCP server bouwen + laden
|
||||
- Demo: eigen tools beschikbaar in alle MCP-clients
|
||||
|
||||
**Daarna in deze leerlijn:**
|
||||
- Les 17: Externe APIs in diepte (OAuth, webhooks, paid APIs)
|
||||
- Les 18: Supabase Auth + RLS — multi-user apps
|
||||
**Volgende les (Les 14): Agents**
|
||||
- LLM in een loop met tools, autonoom 20-50 stappen
|
||||
- `ToolLoopAgent`, `stopWhen`, `prepareStep`
|
||||
- Research-agent from scratch
|
||||
- Wanneer wel/niet agents gebruiken
|
||||
|
||||
**Vragen? Feedback?**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user