Onder de motorkap
Hoe werkt de WK 2026 Pool?
Van live-uitslagen tot automatische puntentelling en dagelijkse mails: alles gebouwd met AI, draaiend op moderne cloudinfrastructuur.
Hoe is deze pool gemaakt?
🤖
Gebouwd met Claude, van Anthropic
De volledige site is gebouwd met behulp van Claude Code en Claude chat van Anthropic. Dat betekent: geen team van ontwikkelaars, geen maanden bouwtijd. Ik beschreef wat ik wilde, en Claude schreef de code, bedacht de structuur en loste problemen op: een digitale bouwpartner die nooit slaapt.
💬
Claude chat, Claude Code en agentic werken
Er zijn drie manieren waarop Claude is ingezet. Claude chat voor ideevorming, ontwerp en teksten. Claude Code (een tool in de terminal) om direct code te schrijven, bestanden aan te passen en te deployen. En agentic werken: Claude voert meerdere stappen achter elkaar uit, zonder dat ik elke regel hoef te controleren.
Hoe weet de pool de uitslag?
Live-uitslagen via football-data.org
Elke wedstrijd wordt automatisch gevolgd via football-data.org, een openbare dataset met alle WK-wedstrijden, uitslagen, groepsstanden en statistieken. De pool haalt om de paar minuten de nieuwste stand op, helemaal automatisch.
🔎
Cross-check met FotMob & api-football
Eén databron is soms onbetrouwbaar — bij een groot toernooi sluipen er wel eens fouten in. Daarom valideert de pool elke afgelopen wedstrijd tegen twee secundaire bronnen: eerst FotMob (gratis, dekt alle 2026-data) en daarna api-football als die data heeft (alleen voor seizoenen die in het abonnement zitten — anders wordt-ie genegeerd). Bij verschil wint de eerste bron die data levert: in de praktijk corrigeert FotMob de hoofdbron als die de mist in gaat.
⚠️
Vlag bij betwiste uitslagen
Wanneer de bronnen het niet eens zijn, krijgt de wedstrijd een klein oranje ⚠ icoon naast de uitslag. Hover (of tik) op het icoon om te zien welke bron is gebruikt en wat de andere bron beweerde. De punten worden automatisch herberekend op basis van de overrulende bron, dus je ziet je stand altijd kloppen met de meest betrouwbare uitslag.
🧮
Punten automatisch berekend
Zodra een wedstrijd is afgelopen, vergelijkt de pool jouw voorspelling met de echte uitslag. Exact goed? 5 punten. Doelpuntenverschil klopt? 3 punten. Winnaar goed? 2 punten. Dit alles gebeurt zonder dat iemand er aan te pas komt.
Hoe werkt het inloggen?
✉️
Geen wachtwoord: magic link via Postmark
Je logt in zonder wachtwoord. Je vult je e-mailadres in, en ontvangt een persoonlijke link. Klik je daarop, dan ben je direct ingelogd: veilig en zonder gedoe met wachtwoorden. Die mails worden verstuurd via Postmark, een professionele e-maildienst.
📬
Dagelijkse update-mail
Na de laatste wedstrijd van de dag stuur de pool automatisch een e-mail naar alle deelnemers: met de uitslagen, de huidige stand en hoeveel punten iedereen die dag pakte. Dit gebeurt helemaal vanzelf, geen handmatig werk.
Waar draait dit allemaal op?
☁️
Alles in de cloud
De pool draait volledig op een modern cloudplatform. Daar staat de database (alle voorspellingen, punten en gebruikers), de server (die alle verzoeken afhandelt) én de website zelf. Één plek, altijd online.
Slimme extras: live data van buiten
🌤️
Weer bij elke wedstrijd
Via Open-Meteo zie je bij elke wedstrijd de verwachte temperatuur en het weer op het stadion. Voor wedstrijden die ver in de toekomst liggen, toont de pool historische klimaatdata als indicatie.
🌍
Landeninfo & vlaggen
Via RestCountries toont de pool de bevolkingsgrootte van elk WK-land. Vlaggen komen van Flagpedia, een gratis CDN met vlagafbeeldingen voor alle landen. Samen geven ze elke wedstrijdkaart direct visuele context.
🕐
Lokale stadion-tijd
Het WK speelt in drie tijdzones (VS, Canada, Mexico). Bij de komende wedstrijden zie je naast de NL-tijd ook de lokale tijd in het stadion, zodat je altijd weet of het midden in de nacht is bij het stadion.
🌙
Dag- of avondwedstrijd
Via de Sunrise-Sunset API berekent de pool of een wedstrijd overdag of 's avonds gespeeld wordt. Elk stadion heeft zijn eigen coördinaten, en het icoon (☀️ of 🌙) verschijnt automatisch op de wedstrijdkaart.
⛰️
Stadionhoogte
De drie Mexicaanse stadions liggen op grote hoogte: Azteca op 2.240m, Akron op 1.566m en BBVA op 538m. Dat beïnvloedt het spel. Wedstrijdkaarten tonen een hoogte-badge zodat je dat in je voorspelling kunt meewegen.
🏟️
Stadionfoto's via Wikipedia
Bij elke wedstrijd zie je een foto van het stadion. Die foto's worden live opgehaald via de Wikipedia REST API: een gratis, sleutelvrije API die per stadionpagina direct een thumbnail retourneert. De resultaten worden 30 dagen gecached in je browser.
Teamlogo's en vormgids via TheSportsDB
Bij komende wedstrijden toont de pool het officiële logo van elke club of nationale ploeg, plus een vormgids met de laatste vijf resultaten (W/G/V). Die komen van TheSportsDB, een gratis sport-database API. Invul je een topscorer? Dan zoekt de pool direct een spelerfoto op.

Let op: TheSportsDB levert alleen visuele content (logo's, foto's, vlaggen). Wedstrijduitslagen komen van football-data.org / FotMob / api-football — zie de sectie hieronder over die drie bronnen.
Wat maakt dit bijzonder?
Dit is wat AI al kan, vandaag
Wat je hier gebruikt is volledig gebouwd door één persoon met AI, in een fractie van de tijd die het vroeger had gekost. Geen team van tien ontwikkelaars, geen half jaar bouwtijd, geen ton aan kosten. Een volwaardige webapplicatie met database, live-koppeling, automatische mails, groepjes en puntentelling: gemaakt met AI als bouwpartner.

Dat is precies het punt van dit project: niet laten zien hoe slim de technologie is, maar hoe toegankelijk het al geworden is.
AI-tooling
🧠
Claude claude-sonnet-4 (Anthropic)
Het project is gebouwd met Claude claude-sonnet-4 (Anthropic), het model dat ook in Claude Code wordt ingezet. Claude is gebruikt in drie modi:
Claude chat: architectuurbeslissingen, UI-ontwerp, copywriting, debugging hypotheses.
Claude Code (CLI): directe codewijzigingen, file edits, git commits en cloud deployment vanuit de terminal.
Agentic mode: meerdere stappen in één sessie: bestanden lezen, aanpassen, testen en deployen zonder handmatige tussenkomst.
🔁
Agentic ontwikkelflow
Typische sessie: ik beschrijf een feature in natural language. Claude leest de relevante bronbestanden, schrijft de implementatie (API-route + frontend + CSS), staged en commit de wijzigingen en triggert de deployment. De hele cyclus van idee tot productie duurt gemiddeld 5 tot 15 minuten per feature.
Tech-stack
Laag Technologie Hosting / provider
Database PostgreSQL Managed cloud database
API server Node.js + Express Cloud platform (containerized)
Frontend Vanilla HTML / CSS / JS Geserveerd via dezelfde Express-app
Auth JWT + magic link (passwordless) Eigen implementatie + sessions-tabel
E-mail Postmark SaaS, transactionele + bulk streams
Wedstrijddata football-data.org + FotMob + api-football SaaS, gratis plan (10 req/min)
Weer Open-Meteo + ERA5 archief Open, geen registratie
Landendata RestCountries + Flagpedia CDN Open, geen registratie
Teamlogo's / vorm TheSportsDB (gratis tier) Logo's, vormgids (W/G/V), spelerfoto's - gecached 7d
Stadionfoto's Wikipedia REST API Open, geen registratie, CORS-vriendelijk - gecached 30d
Scheduler node-cron In-process, draait binnen de applicatiecontainer
Versioning Git + GitHub GitHub repo, automatisch deployen bij push
AI-tooling Claude claude-sonnet-4 (chat + Code) Anthropic: claude.ai + CLI
Architectuur

De applicatie is een klassieke monolithische Express-app. De frontend is statisch (gekopieerd bij deploy), de API draait op dezelfde origin. Geen CDN, geen aparte proxy: alles via één platform.

1
Browser request
Gebruiker opent dewkpool.nl: de server serveert statische HTML/CSS/JS via Express static().
2
API calls via fetch()
Frontend roept /api/* endpoints aan. JWT-token wordt meegestuurd als Bearer header (opgeslagen in localStorage + cookie).
3
Auth middleware
requireAuth verifieert het JWT-token en controleert de sessie-tabel in PostgreSQL. requireAdmin controleert ook is_admin = TRUE.
4
Route handlers
Express routes verwerken de request: queries via de database-pool naar PostgreSQL, response als JSON.
5
Scheduler (achtergrond)
node-cron jobs draaien parallel: live-scores elke 2 min tijdens wedstrijden, dagelijkse update-mail automatisch na laatste wedstrijd van de dag.
Externe datakoppeling: drie databronnen
1. football-data.org — hoofdbron
Alle wedstrijden, uitslagen en groepsstanden worden hier opgehaald. Het gratis plan staat 10 requests per minuut toe; de sync-service heeft een ingebouwde rate-limiter die wacht tussen calls. Dit is waar de schema-data en de eerste live-score vandaan komt.
🔎
2. FotMob — primaire verificatie (gratis)
Na elke afgelopen wedstrijd vraagt de pool de uitslag óók op bij FotMob (gratis publieke API). Verschilt de score met football-data? Dan wint FotMob, de oude score wordt opgeslagen als "alternatief", en de match krijgt een ⚠ vlag in de UI. Punten worden automatisch herberekend.
🛡️
3. api-football — secundaire verificatie
Als FotMob geen data heeft, valt de pool terug op api-football (api-sports.io). Levert die ook geen data voor het seizoen (afhankelijk van het abonnement), dan blijft de football-data score gewoon staan. Er gaat nooit data verloren.
// Automatische sync-momenten Live wedstrijd actief: elke 2 minuten → live-scores bijwerken Wedstrijddag: elke 5 minuten → scores + groepsstanden bijwerken Nachtelijke sync: 03:00 UTC dagelijks → volledig schema + verificatie + punten Na laatste wedstrijd: + 20 min wachttijd → puntenberekening + update-mail

De scheduler detecteert of er live wedstrijden zijn door elke 5 minuten de matches-tabel te controleren op status IN_PLAY of PAUSED. De dagelijkse update-mail triggert automatisch zodra alle wedstrijden van de dag status FINISHED hebben: niet op een vaste tijd. De verificatie tegen FotMob/api-football draait mee in de nachtelijke sync en bij elke handmatige admin-sync.

Externe publieke API-koppelingen
API / CDN Gebruik in dewkpool.nl
football-data.org Hoofdbron: alle 104 wedstrijden, uitslagen en groepsstanden. Schema-data en eerste live-scores
FotMob Primaire verificatiebron: cross-checkt elke afgelopen uitslag tegen de hoofdbron. Bij verschil wint FotMob en wordt de match gevlagd met ⚠
api-football (api-sports.io) Secundaire verificatiebron, alleen ingeroepen als FotMob geen data heeft. Wordt genegeerd zonder fout als het abonnement het seizoen niet dekt
Open-Meteo Weersvoorspelling per stadion op wedstrijddag (temp, neerslag, windcode). Forecast t/m 16 dagen; daarna historisch klimaatgemiddelde via ERA5-archief
RestCountries Bevolkingsgrootte, hoofdstad en regio per WK-land (getoond onder teamname) op wedstrijdkaarten
Flagpedia CDN Vlagafbeeldingen voor alle 48 WK-landen (vervangt emoji-vlaggen) op wedstrijdkaarten en formulieren
Tijdzone (Intl) Lokale wedstrijdtijd per stadion berekend via JavaScript's ingebouwde Intl.DateTimeFormat, geen externe API nodig
Wikipedia REST API Twee toepassingen: (1) klik op een vlag → popup met landenbeschrijving (/page/summary/{land}); (2) stadionfoto's per wedstrijd - thumbnail opgehaald via /page/summary/{stadion} voor alle 16 WK-stadions. CORS-vriendelijk, geen key, gecached 30 dagen in localStorage.
TheSportsDB (gratis tier) Drie functies via het gratis plan (API key 3):
Teamlogo's: searchteams.phpstrTeamBadge, gecached 7 dagen
Vormgids: searchteams.php + eventslast15.php → laatste 5 resultaten (W/G/V)
Spelerfoto: searchplayers.php → foto bij topscorer-invoerveld
Noot: searchvenues.php (stadionfoto's) is Patreon-exclusief - die functie is vervangen door de Wikipedia REST API.
Sunrise-Sunset API Bepaalt per stadion en datum of een wedstrijd overdag of 's avonds gespeeld wordt (☀️ / 🌙). Gebruikt coördinaten van het stadion en vergelijkt met de zonsondergang
Stadionhoogte (hardcoded) Hoogte boven zeeniveau per stadion. Bij meer dan 300m wordt een badge getoond (⛰️). Azteca: 2.240m, Akron: 1.566m, BBVA: 538m - hoogte beïnvloedt het spel
Alle externe koppelingen zijn open beschikbaar en vereisen geen betaalde registratie. Resultaten worden gecached in localStorage zodat herhaalde calls vermeden worden.
E-mail: Postmark
✉️
Twee message streams
Postmark maakt onderscheid tussen transactionele en bulk-mails. De pool gebruikt beide streams:
Transactionele stroom: // Magic links, uitnodigingen (1-op-1) Bulk stroom: // Dagelijkse update-mails (alle deelnemers)

Magic links zijn 30 minuten geldig en eenmalig bruikbaar. Na verificatie wordt een JWT-token aangemaakt (30 dagen geldig) dat opgeslagen wordt in zowel localStorage als een httpOnly-achtige cookie voor maximale compatibiliteit.

Puntentelling: scoring engine

De scoring-logica verwerkt elke FINISHED-wedstrijd en berekent punten voor alle openstaande voorspellingen.

// Bepaal type (groepsfase) pred_home === result_home && pred_away === result_awayMATCH_EXACT (5 ptn) (pred_home - pred_away) === (result_home - result_away)MATCH_DIFF (3 ptn) winnaar correctMATCH_WINNER (2 ptn) andersMATCH_WRONG (0 ptn) // Knockout: hogere inzet Exacte uitslag knockout8 ptn Winnaar correct knockout3 ptn

Poule-eindstand (10/6/3 ptn) en toernooi-voorspellingen (kampioen 15 ptn, topscorer 10 ptn) worden apart verwerkt. Resultaten worden opgeslagen en geaggregeerd in de ranglijst-view.

Deployment pipeline
1
Wijziging in frontend/
HTML/CSS/JS bestanden worden aangepast in frontend/.
2
Frontend synchroniseren
Frontendbestanden worden gekopieerd naar de serveerdirectory zodat Express ze kan serveren.
3
git push → GitHub
Commit naar de main branch op GitHub.
4
Cloud deployment
Het platform bouwt automatisch een nieuwe containerimage, voert een zero-downtime deploy uit en serveert de nieuwe versie op dewkpool.nl.
Beveiliging
Security headers
Standaard HTTP-beveiligingsheaders actief op alle responses
Rate limiting
Limieten per IP op alle endpoints, strenger voor inlogpogingen
Sessiebeheer
Tokens worden zowel lokaal als server-side gevalideerd: uitloggen heeft direct effect
Domeinrestrictie
Alleen verzoeken van het eigen domein worden geaccepteerd
Versleutelde verbindingen
Dataverkeer tussen applicatie en database volledig versleuteld
Veilige queries
Alle database-input via geparametriseerde queries: SQL-injectie niet mogelijk
🤖
Meer weten over AI?
Bekijk ook mijn andere AI-projecten, of neem contact op.
Over de maker Contact