11 KiB
Les 17 — Externe APIs in diepte
Slide Overzicht (Klas A — 3 uur fysiek, demo-driven)
Lesvorm: Tim demonstreert klassikaal. Studenten kijken. Zelf bouwen = huiswerk. Demo-app: Subscription-dashboard (Stripe Checkout + Resend email) Vervolg op: Les 16 — MCP servers Aansluit op: Les 18 — Supabase Auth + RLS
Slide 1: Title
Les 17 — Externe APIs in diepte
Visual: "Les 17" BLUE, "Externe APIs in diepte" BLACK, subtitle "OAuth, webhooks, Stripe en Resend — productie-patronen"
Slide 2: Terugblik
Waar staan we?
Lessen 11-16:
- AI SDK, tool calling, agents, RAG, Cursor+Vercel deploy, MCP
Externe APIs deden we al in Les 15 (PokéAPI): simpel, geen key, GET requests. Productie is anders.
Vandaag — de echte wereld:
- Login met Google/GitHub (OAuth)
- Iemand doet een betaling — server moet dat weten (webhooks)
- Betaalde APIs zoals Stripe en Resend
- Wat als de API faalt? Retry-logic
- Hoe doe je dat veilig en netjes?
Slide 3: Planning
Vandaag — 180 minuten
| Onderwerp | Duur |
|---|---|
| Terugblik + API-types | 15 min |
| Theorie: OAuth basics | 20 min |
| Theorie: Webhooks + verifying | 20 min |
| Live Demo 1 — Stripe Checkout integratie | 25 min |
| Live Demo 2 — Webhook ontvangen + verify | 25 min |
| Pauze | 15 min |
| Live Demo 3 — Resend transactional email | 25 min |
| Live Demo 4 — Rate limit + retry pattern | 15 min |
| Productie checklist | 10 min |
| Lesopdracht + Huiswerk | 10 min |
Slide 4: Drie soorten externe API integratie
Welke heb je nodig?
| Type | Voorbeeld | Wat moet je weten |
|---|---|---|
| Simple fetch | PokéAPI, Open-Meteo | URL + JSON parsing (Les 15) |
| API key in header | OpenAI, Tavily, Anthropic | Env vars, server-side fetch |
| OAuth flow | Google login, GitHub | Redirect dance, tokens, sessies |
| Webhook | Stripe, GitHub, Slack | POST endpoint, signature verify |
Vandaag focus op de laatste twee — daar komen 80% van productie-API-bugs vandaan.
Slide 5: OAuth — wat en waarom
Login met Google / GitHub / etc.
Het probleem: je wilt dat gebruikers inloggen zonder zelf wachtwoorden te beheren.
OAuth flow (kort):
1. User klikt "Login met GitHub"
2. Redirect naar github.com/login/oauth/authorize
3. User logt in op GitHub + geeft toestemming
4. GitHub redirect terug naar JOUW app met code
5. JOUW server exchanged code voor access_token
6. JOUW server haalt user-data op met token
7. JOUW server zet sessie / cookie
In Next.js — drie populaire libraries:
- Auth.js (NextAuth v5) — meest gebruikt
- Better-Auth — moderne challenger, eenvoudiger
- Clerk — paid, alles inclusief
Voor dit vak: Auth.js — open source, gratis, werkt overal.
Slide 6: Webhooks — wat en waarom
Externe service belt JOUW app
Het probleem: soms moet je weten wanneer iets gebeurt op een externe service.
Voorbeeld Stripe:
- User doet betaling op Stripe Checkout
- Stripe verwerkt betaling (zou kunnen falen!)
- Stripe stuurt POST request naar JOUW webhook URL
- Jouw server update database, stuurt email, etc.
Webhook = jouw HTTP endpoint dat door externe service wordt aangeroepen.
Cruciaal: signature verification
Zonder verificatie kan iedereen je webhook URL aanroepen met fake data.
// Stripe doet dit zo:
const sig = request.headers.get("stripe-signature");
const event = stripe.webhooks.constructEvent(
body, sig, process.env.STRIPE_WEBHOOK_SECRET
);
// Throws als signature niet klopt
Slide 7: Wat we vandaag bouwen
Subscription-dashboard
Mini-app: Premium subscription met Stripe + email confirmaties.
Tech:
- Next.js 16
- Stripe (test mode — geen echt geld)
- Resend (transactional email — gratis tier 3000/maand)
- ngrok of
localhost.runvoor lokaal webhook testen
Flow:
[Pricing page] → [Stripe Checkout] → [Webhook] → [Database + Email]
Endpoints we bouwen:
POST /api/checkout— start Stripe sessiePOST /api/webhook/stripe— ontvang Stripe eventsPOST /api/email— verzend Resend mail (intern aangeroepen)
Slide 8: LIVE DEMO 1 — Stripe Checkout
~25 min
Wat ik laat zien:
- Stripe account opzetten (test mode)
- Pricing page met "Subscribe" button
app/api/checkout/route.ts— maak Checkout session
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { priceId, email } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: priceId, quantity: 1 }],
customer_email: email,
success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/pricing`,
});
return Response.json({ url: session.url });
}
- Client redirect naar
session.url - Test in browser met Stripe test cards (4242 4242 4242 4242)
- Stripe Dashboard — sessie zichtbaar, betaling staat als "complete"
Slide 9: LIVE DEMO 2 — Webhook ontvangen
~25 min
Wat ik laat zien:
- Webhook secret ophalen van Stripe Dashboard
app/api/webhook/stripe/route.ts:
import { headers } from "next/headers";
export async function POST(req: Request) {
const body = await req.text(); // RAW body, niet json()
const sig = headers().get("stripe-signature")!;
let event;
try {
event = stripe.webhooks.constructEvent(
body, sig, process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return new Response("Webhook signature failed", { status: 400 });
}
if (event.type === "checkout.session.completed") {
const session = event.data.object;
await supabase.from("subscriptions").insert({
customer_email: session.customer_email,
stripe_session_id: session.id,
status: "active",
});
}
return Response.json({ received: true });
}
- Lokaal testen:
stripe listen --forward-to localhost:3000/api/webhook/stripe - Stripe CLI geeft webhook secret terug — in
.env.local - Doe een test-checkout — zie webhook binnenkomen in CLI én database update
Belangrijk: req.text() voor RAW body — niet req.json(). Signature wordt over bytes berekend.
Slide 10: Pauze
15 min
Slide 11: LIVE DEMO 3 — Resend email
~25 min
Wat ik laat zien:
- Resend account, API key, eigen domain (of
onboarding@resend.devvoor demo) pnpm add resend react-email- Email template als React component:
// emails/WelcomeEmail.tsx
export function WelcomeEmail({ name }: { name: string }) {
return (
<div style={{ fontFamily: "sans-serif" }}>
<h1>Welkom {name}!</h1>
<p>Bedankt voor je aanmelding bij Premium.</p>
</div>
);
}
- Verzend in webhook handler:
import { Resend } from "resend";
import { WelcomeEmail } from "@/emails/WelcomeEmail";
const resend = new Resend(process.env.RESEND_API_KEY!);
await resend.emails.send({
from: "onboarding@resend.dev",
to: session.customer_email!,
subject: "Welkom bij Premium!",
react: WelcomeEmail({ name: session.customer_email!.split("@")[0] }),
});
- Test checkout → webhook → mail komt aan
- Resend dashboard — email zichtbaar in logs, status delivered
Slide 12: LIVE DEMO 4 — Rate limit + retry
~15 min
Wat ik laat zien:
Probleem: externe APIs kunnen falen. Wat doe je?
Pattern 1 — Exponential backoff retry:
async function retry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
for (let i = 0; i < attempts; i++) {
try {
return await fn();
} catch (err) {
if (i === attempts - 1) throw err;
await new Promise(r => setTimeout(r, 2 ** i * 1000));
}
}
throw new Error("unreachable");
}
const data = await retry(() =>
fetch("https://api.example.com/data").then(r => r.json())
);
Pattern 2 — Rate limit met Upstash Ratelimit:
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10s"),
});
// In API route
const { success } = await ratelimit.limit(userId);
if (!success) return new Response("Too many requests", { status: 429 });
Beide patronen zijn cruciaal voor productie. Niet doen = service crash bij verkeerd gedrag.
Slide 13: Productie checklist
Voordat je live gaat
API keys:
- ✓ Test-keys voor preview, prod-keys voor productie
- ✓ Vercel scoping per environment (Les 15)
- ✓ Rotate keys eens per kwartaal
Webhooks:
- ✓ Signature verification ALTIJD
- ✓ Idempotency — zelfde event 2x ontvangen = OK
- ✓ Log alle webhook payloads (helpt bij debug)
- ✓ Return 200 snel — verwerking async
Errors:
- ✓ Exponential backoff retry voor transient fails
- ✓ Rate limit eigen endpoints
- ✓ Monitor met Sentry / LogRocket / Vercel Analytics
Security:
- ✓ Keys nooit in client-bundle (geen
NEXT_PUBLIC_) - ✓ Webhook endpoints accepteren alleen verified signatures
- ✓ User-input validatie (Zod)
Slide 14: Lesopdracht + Huiswerk
Eigen externe API integreren
Lesopdracht (30 min):
- Stripe test account opzetten
- Pricing page + Checkout endpoint
- Doe een test-betaling met test card
- Geen webhook nog — alleen success_url demo
Huiswerk (~2 uur, voor Les 18):
- A: Webhook handler + signature verify
- B: Resend transactional email na betaling
- C: Retry logic voor externe API call
- D:
APIS.mdmet:- Welke externe APIs jouw app gebruikt
- Hoe webhooks getest worden (Stripe CLI of ngrok screenshots)
- Voorbeeld van een failed call + hoe afgevangen
- Eindopdracht-toepassing: welke API ga jij integreren
Bonus: OAuth login met GitHub/Google via Auth.js
Slide 15: Volgende les + Afsluiting
Vragen?
Vandaag gezien:
- Drie soorten externe API integratie
- OAuth flow basics
- Webhooks ontvangen + signature verify
- Stripe Checkout in productie-patroon
- Resend transactional email
- Rate limit + retry
- Productie checklist
Volgende les (Les 18 — laatste!): Supabase Auth + RLS
- Multi-user apps met login
- Magic link / password / social
- RLS policies — per-user data isolation
- Protected routes + middleware
- Combo van alles wat we geleerd hebben
Vragen?
Slide Summary
| # | Title | Type |
|---|---|---|
| 1 | Title | Opening |
| 2 | Terugblik | Recap |
| 3 | Planning | 180-min |
| 4 | 3 soorten API integratie | Theorie |
| 5 | OAuth basics | Theorie |
| 6 | Webhooks + verifying | Theorie |
| 7 | Wat we bouwen | Intro |
| 8 | DEMO 1 — Stripe Checkout | Demo |
| 9 | DEMO 2 — Webhook ontvangen | Demo |
| 10 | Pauze | Break |
| 11 | DEMO 3 — Resend email | Demo |
| 12 | DEMO 4 — Rate limit + retry | Demo |
| 13 | Productie checklist | Reflectie |
| 14 | Lesopdracht + Huiswerk | Praktijk |
| 15 | Afsluiting | Closing |
Bronnen
- Stripe Checkout docs: https://docs.stripe.com/checkout
- Stripe webhooks: https://docs.stripe.com/webhooks
- Stripe CLI: https://docs.stripe.com/stripe-cli
- Resend: https://resend.com/docs
- React Email: https://react.email/
- Auth.js: https://authjs.dev/
- Upstash Ratelimit: https://upstash.com/docs/oss/sdks/ts/ratelimit
- Better-Auth: https://www.better-auth.com/