Files
novi-lessons/Les14-Cursor-Vercel-Deploy/Les14-Lesstof.md
2026-06-09 18:02:04 +02:00

17 KiB

Les 13 — Lesstof

Externe APIs + Cursor agents + Vercel deploy

Vak: AI-Assisted Development Opleiding: NOVI Hogeschool Utrecht Vorige les: Les 12 — Tool Calling Volgende les: Les 14 — Agents


Inhoud

  1. Externe APIs in Next.js
  2. Server- vs client-side fetching
  3. Environment variabelen
  4. Cursor agents — Composer vs Background
  5. Vercel deployments
  6. Preview deployments per branch
  7. GitHub Actions CI
  8. Branch protection
  9. Werkstroom — feature van begin tot productie
  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

// 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. Vier manieren om een externe API te fetchen

Next.js geeft je per fetch-call de keuze hoe je data wilt renderen. Voor 95% van je werk komt het neer op drie server-modes plus client-side. De code verandert minimaal — meestal één optie in je fetch().

2.1 Static (build-time)

// app/page.tsx — Server Component
async function getPokemon() {
  const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151");
  return res.json();
}
  • Gefetched tijdens next build, daarna HTML voor altijd
  • Geen server-werk per request → snelste optie
  • Data ververst alleen bij een nieuwe build (= nieuwe deploy)
  • Goed voor: marketing-pagina's, blogs, productlijsten, alles dat zelden verandert
  • Niet voor: prijzen die per minuut updaten, user-specifieke data

2.2 ISR — Incremental Static Regeneration

const res = await fetch(URL, { next: { revalidate: 3600 } });
  • Eerste request: zelfde als static (HTML uit cache)
  • Na 3600 seconden: volgende request krijgt nog steeds cache, maar achter de schermen wordt de pagina vernieuwd
  • Goed voor: Pokédex detail-pages, een nieuws-feed die niet realtime hoeft, productdetails
  • De sweet spot tussen snel en redelijk vers

2.3 Dynamic (request-time / SSR)

const res = await fetch(URL, { cache: "no-store" });
  • Bij elke request opnieuw gefetched op de server
  • Altijd 100% vers, maar trager (geen cache)
  • Goed voor: dashboards met live data, user-specifieke views, anything per-request
  • Let op cost: elke request = serverless function call

2.4 Client-side (useEffect)

"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>;
}
  • Fetch gebeurt in de browser na hydration
  • Goed voor user-interactie, real-time, infinite scroll
  • Cruciaal: API-keys staan in de browser als je hier rechtstreeks naar een externe API roept. Alleen voor publieke APIs, of via een eigen /api/... proxy-route.

Spiekbriefje

Mode fetch() optie Wanneer
Static fetch(URL) (default) Data verandert per deploy
ISR { next: { revalidate: N } } Data verandert traag
Dynamic { cache: "no-store" } Data verandert per request
Client useEffect + fetch User-interactie / live
Tag-based { next: { tags: ["x"] } } Triggered revalidation via revalidateTag()

3. Environment variabelen — server-only vs client-exposed

Twee soorten variabelen

Type Voorbeeld Waar beschikbaar Veiligheid
Server-only OPENAI_API_KEY, DATABASE_URL Alleen op de server Geheim — nooit zichtbaar voor de browser
Client-exposed NEXT_PUBLIC_APP_ENV, NEXT_PUBLIC_APP_URL Server + client bundle Publiek — iedereen kan ze zien in DevTools

Regel: alles met NEXT_PUBLIC_ prefix wordt ingebakken in de browser-bundle. Zonder prefix = server-only.

// ✅ Veilig — server-side gebruik
process.env.OPENAI_API_KEY

// ❌ NOOIT
"use client";
const key = process.env.NEXT_PUBLIC_OPENAI_KEY; // staat in de HTML response

3.1 Vercel — drie omgevingen, drie scopes

Op Vercel heb je per project drie environments. Bij elke env-var kies je in welke scopes hij wordt geïnjecteerd:

Scope Wordt gebruikt bij URL-patroon
Production Push naar main je-app.vercel.app
Preview Push naar elke andere branch je-app-git-{branch}-{user}.vercel.app
Development Lokaal (pnpm dev) localhost:3000

3.2 Per-omgeving andere waarden

In Vercel → Project → Settings → Environment Variables zet je per variabele drie keer een waarde:

Var Production Preview Development
DATABASE_URL productie-DB staging-DB lokale DB
OPENAI_API_KEY echte key test-key (lage limit) jouw key
STRIPE_KEY sk_live_... sk_test_... sk_test_...
NEXT_PUBLIC_APP_URL je-app.com preview-URL http://localhost:3000

Hoe in te stellen: in de UI: Add new → vink alleen de juiste environment(s) aan → Save.

Belangrijke veiligheidsregels:

  • Productie-secrets (echte API keys, prod-DB-password): alleen Production vinken
  • Preview krijgt een test-variant, anders kunnen experimentele branches je productie raken
  • Development: vercel env pull om Vercel's dev-vars naar je .env.local te syncen, of zelf invullen

3.3 Verandering pakt pas door bij nieuwe build

Bestaande deploys zien een nieuwe env-var niet automatisch. Na het toevoegen of wijzigen: redeploy (Deployments → ··· → Redeploy) of push een nieuwe commit.


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

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:

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