488 lines
14 KiB
Markdown
488 lines
14 KiB
Markdown
# Les 13 — Lesstof
|
|
## Externe APIs + Cursor agents + Vercel deploy
|
|
|
|
**Vak:** AI-Assisted Development
|
|
**Opleiding:** NOVI Hogeschool Utrecht
|
|
**Vorige les:** Les 14 — RAG + Embeddings
|
|
**Volgende les:** Les 14 — RAG + Embeddings
|
|
|
|
---
|
|
|
|
## Inhoud
|
|
|
|
1. [Externe APIs in Next.js](#1-externe-apis-in-nextjs)
|
|
2. [Server- vs client-side fetching](#2-server--vs-client-side-fetching)
|
|
3. [Environment variabelen](#3-environment-variabelen)
|
|
4. [Cursor agents — Composer vs Background](#4-cursor-agents--composer-vs-background)
|
|
5. [Vercel deployments](#5-vercel-deployments)
|
|
6. [Preview deployments per branch](#6-preview-deployments-per-branch)
|
|
7. [GitHub Actions CI](#7-github-actions-ci)
|
|
8. [Branch protection](#8-branch-protection)
|
|
9. [Werkstroom — feature van begin tot productie](#9-werkstroom--feature-van-begin-tot-productie)
|
|
10. [Productie-overwegingen](#10-productie-overwegingen)
|
|
|
|
---
|
|
|
|
## 1. Externe APIs in Next.js
|
|
|
|
Een externe API is een HTTP-endpoint van iemand anders waar je data of functionaliteit kunt ophalen. Voor de meeste apps die je gaat bouwen heb je er minstens één nodig.
|
|
|
|
### Wat valt eronder
|
|
|
|
- **Data-APIs** — PokéAPI (Pokemon), Open-Meteo (weer), GitHub API (repos), TMDB (films)
|
|
- **AI-providers** — OpenAI, Anthropic, Tavily (web search)
|
|
- **Payment** — Stripe, Mollie
|
|
- **Auth** — Clerk, Auth0, Supabase Auth
|
|
- **Email** — Resend, Postmark, SendGrid
|
|
|
|
### Drie smaken qua authenticatie
|
|
|
|
- **Geen key** — PokéAPI, Open-Meteo. Direct fetchen.
|
|
- **API key in URL of header** — Tavily, TMDB. Key beheren als env-variabele.
|
|
- **OAuth flow** — Google, GitHub. Complexer, vaak via libraries.
|
|
|
|
### Basis-pattern in Next.js
|
|
|
|
```typescript
|
|
// Server Component
|
|
async function getPokemon(name: string) {
|
|
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`);
|
|
if (!res.ok) throw new Error("Niet gevonden");
|
|
return res.json();
|
|
}
|
|
|
|
export default async function Page({ params }) {
|
|
const data = await getPokemon(params.name);
|
|
return <div>{data.name}</div>;
|
|
}
|
|
```
|
|
|
|
Drie dingen om te onthouden:
|
|
|
|
- Server Components mogen `async` zijn — `await` direct in component
|
|
- Errors moet je zelf afvangen (`!res.ok`)
|
|
- `fetch` in een Server Component **wordt automatisch gecached** door Next.js
|
|
|
|
---
|
|
|
|
## 2. Server- vs client-side fetching
|
|
|
|
### Server-side (Server Component of API route)
|
|
|
|
```typescript
|
|
// app/pokemon/[name]/page.tsx
|
|
async function getPokemon(name) {
|
|
const res = await fetch(`https://pokeapi.co/api/v2/pokemon/${name}`, {
|
|
next: { revalidate: 3600 }, // cache 1 uur
|
|
});
|
|
return res.json();
|
|
}
|
|
```
|
|
|
|
**Voordelen:**
|
|
- API key blijft op de server (nooit in client-bundle)
|
|
- Caching gratis via Next.js
|
|
- SEO-vriendelijk (HTML met data terug)
|
|
- Snellere TTFB (geen extra roundtrip)
|
|
|
|
**Wanneer gebruiken:** initiële data, lijsten, detail-pagina's.
|
|
|
|
### Client-side (`useEffect` of `useState` met fetch)
|
|
|
|
```typescript
|
|
"use client";
|
|
import { useEffect, useState } from "react";
|
|
|
|
export function LivePokemon({ name }) {
|
|
const [data, setData] = useState(null);
|
|
useEffect(() => {
|
|
fetch(`/api/pokemon/${name}`).then((r) => r.json()).then(setData);
|
|
}, [name]);
|
|
return data ? <div>{data.name}</div> : <p>Loading...</p>;
|
|
}
|
|
```
|
|
|
|
**Wanneer gebruiken:**
|
|
- User-interactie (search, filter, infinite scroll)
|
|
- Real-time updates (polling, websockets)
|
|
- Data die per user verschilt en niet op server kan
|
|
|
|
> **Belangrijke regel:** API keys NOOIT in client-code. Voor authenticated externe APIs: maak een API-route in `app/api/...` als proxy.
|
|
|
|
### Cache-opties bij `fetch`
|
|
|
|
```typescript
|
|
fetch(url, { cache: "force-cache" }) // permanent (default Next.js)
|
|
fetch(url, { next: { revalidate: 3600 } }) // ISR — herval na N seconden
|
|
fetch(url, { cache: "no-store" }) // nooit cachen (per request)
|
|
fetch(url, { next: { tags: ["pokemon"] } }) // tag-based revalidation
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Environment variabelen
|
|
|
|
### Drie scopes in Next.js + Vercel
|
|
|
|
| Scope | Wanneer | Waar |
|
|
|-------|---------|------|
|
|
| `OPENAI_API_KEY` | Server-only | Process env op server |
|
|
| `NEXT_PUBLIC_APP_URL` | Server + client | In client-bundle (zichtbaar in DevTools!) |
|
|
| `.env.local` | Lokaal alleen | Niet committen, `.gitignore` |
|
|
|
|
### Regel
|
|
|
|
**Alles wat geheim is — zonder `NEXT_PUBLIC_` prefix.** Met prefix = in client bundle = iedereen kan het zien.
|
|
|
|
```typescript
|
|
// ✅ Server-only — veilig
|
|
process.env.OPENAI_API_KEY
|
|
|
|
// ❌ NEVER doen
|
|
"use client";
|
|
const key = process.env.NEXT_PUBLIC_OPENAI_KEY; // staat in HTML response!
|
|
```
|
|
|
|
### Vercel environment scoping
|
|
|
|
Op Vercel heb je drie environments:
|
|
|
|
- **Production** — deploys vanaf `main`
|
|
- **Preview** — alle andere branches + PRs
|
|
- **Development** — lokaal (`vercel env pull` om te syncen)
|
|
|
|
Per env-var kies je in welke scopes hij beschikbaar is. Handig:
|
|
|
|
```
|
|
OPENAI_API_KEY_PROD → Production scope
|
|
OPENAI_API_KEY_TEST → Preview + Development scope
|
|
```
|
|
|
|
Op die manier verbruiken preview deploys niet je productie-quota.
|
|
|
|
---
|
|
|
|
## 4. Cursor agents — Composer vs Background
|
|
|
|
Cursor heeft twee soorten "agents". Niet hetzelfde, niet uitwisselbaar.
|
|
|
|
### Composer
|
|
|
|
- Lokaal in je editor (`Cmd+I` / `Ctrl+I`)
|
|
- **Synchroon** — jij wacht, agent werkt, jij ziet diffs
|
|
- Multi-file edits in één keer
|
|
- Terminal-commands met approval
|
|
- Voor: pair programming, snelle changes, exploreren
|
|
|
|
### Background Agent
|
|
|
|
- Runt in **Cursor's cloud sandbox**
|
|
- **Asynchroon** — jij geeft prompt, gaat iets anders doen
|
|
- Maakt zelf een branch + commit + opent PR
|
|
- Kan tests draaien, dependencies installeren
|
|
- Verbonden via Cursor cloud (eenmalige setup)
|
|
- Voor: welomschreven features, parallel werk, PR-prep
|
|
|
|
### Aanroep
|
|
|
|
```
|
|
Cursor Composer: Cmd+I (Mac) of Ctrl+I (Win/Linux)
|
|
Cursor Chat: Cmd+L (kan switchen naar Agent mode)
|
|
Background Agent: Cmd+Shift+P → "Open Background Agent"
|
|
Of via Slack-integratie
|
|
```
|
|
|
|
### Mentale model
|
|
|
|
- **Composer = pair programming.** Jij + AI samen aan dezelfde plek tegelijk.
|
|
- **Background Agent = delegeren.** Je geeft taak, gaat iets anders doen, komt terug als-ie klaar is.
|
|
|
|
### Wanneer welke
|
|
|
|
| Scenario | Tool |
|
|
|----------|------|
|
|
| Snel iets aanpassen tijdens coden | Composer |
|
|
| Refactor over 5 files — wil zien wat hij doet | Composer |
|
|
| Onbekend gebied / leren | Composer |
|
|
| Welomschreven feature, can-be-async | Background Agent |
|
|
| Parallel werken aan 3 features | 3x Background Agent |
|
|
| Tijdens vergadering PR voorbereiden | Background Agent |
|
|
|
|
### Hoe schrijf je een goede Background Agent prompt
|
|
|
|
Een Background Agent ziet je niet — je moet specifiek zijn.
|
|
|
|
**Slecht:**
|
|
> "Voeg search toe."
|
|
|
|
**Goed:**
|
|
> "Maak een nieuwe branch `feature/search`. Voeg een controlled input toe bovenaan `app/page.tsx`. Filter de Pokémon-lijst op naam terwijl gebruiker typt. Case-insensitive. Gebruik Tailwind. Houd het stijl-consistent met bestaande cards. Push de branch en open een PR met titel 'Add search bar' en korte beschrijving in PR body."
|
|
|
|
Specifiek: bestandsnamen, gedrag, edge cases, output-formaat (branch + PR + titel).
|
|
|
|
---
|
|
|
|
## 5. Vercel deployments
|
|
|
|
Vercel is hosting platform door Next.js team (zelfde bedrijf). Voor Next.js apps de simpelste deploy.
|
|
|
|
### Eerste deployment
|
|
|
|
1. Push je repo naar GitHub
|
|
2. `vercel.com` → Add New → Import Git Repository
|
|
3. Kies repo, klik **Deploy**
|
|
4. ~60-90s later: live URL
|
|
|
|
**Geen config nodig.** Vercel detecteert Next.js automatisch.
|
|
|
|
### Build settings (als je ze nodig hebt)
|
|
|
|
- Framework: Next.js (auto)
|
|
- Build command: `pnpm build` (auto)
|
|
- Output directory: `.next` (auto)
|
|
- Install command: `pnpm install`
|
|
- Root directory: alleen aanpassen als app in subfolder zit (monorepo)
|
|
|
|
### CLI alternatief
|
|
|
|
```bash
|
|
pnpm i -g vercel
|
|
vercel login
|
|
vercel # preview deploy van current dir
|
|
vercel --prod # production deploy
|
|
vercel env pull # download .env.local van Vercel
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Preview deployments per branch
|
|
|
|
Hier komt de magie. Vercel maakt automatisch een preview deploy voor **elke branch en elke PR**.
|
|
|
|
### Hoe werkt het
|
|
|
|
```
|
|
git push origin main → Production deploy
|
|
(je-app.vercel.app)
|
|
|
|
git push origin feature/search → Preview deploy
|
|
(je-app-git-feature-search-user.vercel.app)
|
|
|
|
PR open → Vercel comment in PR:
|
|
"🚀 Preview: <URL>"
|
|
```
|
|
|
|
### Wat krijg je hiermee
|
|
|
|
- **Reviewers** klikken op de URL in de PR, testen de feature live
|
|
- **Background Agents** maken PR → Vercel zet preview neer → klikbaar
|
|
- **Stakeholders** zien een feature voor merge, niet alleen screenshots
|
|
- **Geen "werkt op mijn laptop"** — alles draait op Vercel infra
|
|
|
|
### Belangrijk om in te stellen
|
|
|
|
- **Environment scoping:** preview-deploys mogen geen productie-keys gebruiken — gebruik aparte env vars per scope
|
|
- **Database:** in productie wijs je naar prod-DB, in preview kun je naar test-DB wijzen — meer dan een Vercel-feature, eigen architectuur
|
|
|
|
### Comment-bot
|
|
|
|
Vercel installeert een GitHub App. Die plakt een comment op je PR met:
|
|
|
|
```
|
|
🚀 Preview Deployment: https://...
|
|
✅ Build successful — 1m 23s
|
|
View build log
|
|
```
|
|
|
|
Per push naar de branch wordt deze comment ge-update.
|
|
|
|
---
|
|
|
|
## 7. GitHub Actions CI
|
|
|
|
Een **CI** (Continuous Integration) check draait automatisch op je code. Minimaal: lint + build.
|
|
|
|
### Waarom
|
|
|
|
- Voor merge weet je: code lint clean, types kloppen, build slaagt
|
|
- Cursor Background Agent maakt PR → CI draait → groene check of feedback
|
|
- Voorkomt dat broken code in main komt
|
|
|
|
### Minimaal workflow
|
|
|
|
`.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 --frozen-lockfile
|
|
- run: pnpm lint
|
|
- run: pnpm build
|
|
```
|
|
|
|
### Wat gebeurt er
|
|
|
|
1. **Trigger:** PR open of update, of push naar `main`
|
|
2. **Runner:** Ubuntu container op GitHub infra
|
|
3. **Stappen:**
|
|
- Checkout code
|
|
- Install pnpm + Node 20
|
|
- Install dependencies
|
|
- Run lint (faalt op fouten)
|
|
- Run build (faalt op TypeScript errors)
|
|
4. **Status:** groene check of rode X op de PR
|
|
|
|
### Tijdsbudget
|
|
|
|
Voor een kleine Next.js app: 1-2 minuten. Voor grote apps met tests: 3-10 minuten.
|
|
|
|
### Caching
|
|
|
|
Met `cache: pnpm` herbruikt GH Actions de node_modules-cache tussen runs — scheelt 60-80% tijd.
|
|
|
|
---
|
|
|
|
## 8. Branch protection
|
|
|
|
Standaard kan iedereen pushen naar main. Voor productie wil je dat niet.
|
|
|
|
### Branch protection rules (op GitHub)
|
|
|
|
1. Repo settings → Branches → Add rule
|
|
2. Branch name pattern: `main`
|
|
3. Check: **Require a pull request before merging**
|
|
4. Check: **Require status checks to pass before merging** → select CI workflow
|
|
5. Check: **Do not allow bypassing the above settings**
|
|
|
|
### Effect
|
|
|
|
- Niemand kan direct naar `main` pushen — moet via PR
|
|
- PR kan pas merge als CI groen is
|
|
- Force-push naar main geblokkeerd
|
|
- Background Agents werken automatisch via PRs (zo bedoeld)
|
|
|
|
### Voor solo-projecten
|
|
|
|
Beetje overkill, maar oefenen is goed. En het voorkomt 'fluitje van een cent' bugs naar main.
|
|
|
|
---
|
|
|
|
## 9. Werkstroom — feature van begin tot productie
|
|
|
|
De volledige flow zoals je 'm in dit vak gebruikt:
|
|
|
|
```
|
|
1. Idee voor feature
|
|
↓
|
|
2. Background Agent prompt (specifiek!)
|
|
↓
|
|
3. Cursor opent branch + maakt commits + opent PR
|
|
↓
|
|
4. GitHub Actions CI draait — lint + build
|
|
↓
|
|
5. Vercel zet preview deploy neer — URL in PR comment
|
|
↓
|
|
6. Jij/team reviewt: code-diff + live preview
|
|
↓
|
|
7. Eventueel: commentaar in PR, Background Agent retried
|
|
↓
|
|
8. Merge PR → main
|
|
↓
|
|
9. Vercel productie-deploy is automatisch
|
|
↓
|
|
10. Klaar — live op je-app.vercel.app
|
|
```
|
|
|
|
Tijdsindicatie voor één feature: 10-30 minuten end-to-end (afhankelijk van complexiteit).
|
|
|
|
### Wat als CI rood is
|
|
|
|
- Klik op rode X in PR → naar GitHub Actions logs
|
|
- Lees error: lint-error, build-error, of test-failure
|
|
- Twee opties:
|
|
- **Composer:** open feature branch lokaal, fix met Composer, push
|
|
- **Background Agent:** nieuwe prompt: "fix de CI error in deze PR — lees de logs en repareer"
|
|
|
|
### Wat als preview niet werkt
|
|
|
|
- Open Vercel dashboard → project → Deployments → klik op preview build
|
|
- Build logs lezen
|
|
- Vaak: env-var mist (Vercel preview scope leeg)
|
|
- Of: dependency die op Vercel niet beschikbaar is
|
|
|
|
---
|
|
|
|
## 10. Productie-overwegingen
|
|
|
|
### Costs
|
|
|
|
| Service | Free tier | Wanneer betalen |
|
|
|---------|-----------|-----------------|
|
|
| **Vercel** | Hobby tier — gratis voor persoonlijk gebruik | Commercieel of >100GB bandwidth |
|
|
| **GitHub Actions** | 2000 min/maand gratis (public repos unlimited) | Bij grote teams of veel runs |
|
|
| **Cursor** | Hobby (gratis), Pro ($20/mnd), Business | Background Agents zitten in Pro/Business |
|
|
| **PokéAPI** | Volledig gratis, geen rate-limit issues | n.v.t. |
|
|
|
|
### Performance
|
|
|
|
Vercel doet veel automatisch:
|
|
- Edge CDN voor static assets
|
|
- ISR (Incremental Static Regeneration) voor `revalidate`
|
|
- Image optimization via `next/image`
|
|
- Automatische gzip/brotli
|
|
|
|
Voor sub-1s page loads: meestal niks doen.
|
|
|
|
### Observability
|
|
|
|
- **Vercel Analytics** — page views + Core Web Vitals (gratis tier beperkt)
|
|
- **Vercel Logs** — server-side console.log zichtbaar
|
|
- **Sentry / LogRocket** — externe tools voor error tracking
|
|
- **GitHub Actions logs** — bewaard 90 dagen
|
|
|
|
### Security
|
|
|
|
- API keys NOOIT in client (geen `NEXT_PUBLIC_`)
|
|
- `.env.local` in `.gitignore` (vooraf checken!)
|
|
- Rotate keys regelmatig — vooral na PR's van Background Agents
|
|
- Vercel environment scoping om dev/prod te scheiden
|
|
|
|
### Rollback
|
|
|
|
Als productie kapot is:
|
|
1. Vercel dashboard → Deployments
|
|
2. Vorige werkende deploy → "Promote to Production"
|
|
3. Klaar, ~10 seconden — geen rebuild nodig
|
|
|
|
---
|
|
|
|
## Bronnen
|
|
|
|
- **Next.js fetching:** https://nextjs.org/docs/app/building-your-application/data-fetching
|
|
- **Next.js caching:** https://nextjs.org/docs/app/building-your-application/caching
|
|
- **PokéAPI:** https://pokeapi.co/
|
|
- **Cursor docs:** https://docs.cursor.com/
|
|
- **Cursor Background Agents:** https://docs.cursor.com/background-agent
|
|
- **Vercel docs:** https://vercel.com/docs
|
|
- **Vercel preview deployments:** https://vercel.com/docs/deployments/preview-deployments
|
|
- **Vercel environment variables:** https://vercel.com/docs/environment-variables
|
|
- **GitHub Actions:** https://docs.github.com/en/actions
|
|
- **GitHub branch protection:** https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches
|