NL EN
Region: europe-west4 [v2.1.0] | status: online!
retail_platform.tf jaridata_dev.py cro_pipeline.py
// projects / jaridata_dev.py

jaridata.dev productie-portfolio platform

typeportfolio platform
statuslive
year2025
TL;DR
Een productie-achtige FastAPI/Jinja2 SSR-app met NL/EN-routes, canonical/hreflang SEO, content-gedreven cases, token-based themes, vanilla JS, Cloudflare-only origin protection, Docker, Terraform/Cloud Run en CI-gates.
0
testdekking
0
Lighthouse SEO
0
edge-responsetijd
0
JS-framework overhead
PythonFastAPIJinja2Pydantic settings
CSS tokensJinja partialsVanilla JSSSR HTML
DockerCloud RunTerraformGitHub ActionsCloudflare

Een persoonlijk portfolio gebouwd met dezelfde engineeringgewoontes die het wil laten zien.

Context & probleem

Een portfolio kan makkelijk eindigen als een statische brochure: snel online, maar technisch weinigzeggend. Voor jaridata.dev is bewust het omgekeerde gekozen. De site moet laten zien hoe ik software, data en cloudwerk structureer: kleine onderdelen, expliciete contracten, reproduceerbare builds en duidelijke guardrails.

Dat betekent niet dat elke persoonlijke site een zwaar platform moet worden. Hier is de extra techniek functioneel: de site bevat meertalige routes, case-content, SEO-metadata, themegedrag, security headers, edge/origin-afscherming en deployment-infrastructuur. Dat zijn precies de onderdelen waarop productie-apps meestal rommelig worden als ze later pas worden toegevoegd.

De vraag was dus niet: "hoe maak je een mooie homepage?", maar: "hoe bouw je een portfolio dat dezelfde engineeringdiscipline laat zien als de projecten die het beschrijft?"

Architectuur

De repository is opgezet als een kleine server-rendered applicatie:

Application
FastAPI routes → Jinja2 templates → locale-aware SSR pages

Content
cases.py + i18n.py → portfolio cards, case pages, chrome copy

Frontend
tokens.css → components/pages CSS → vanilla JS enhancements

Delivery
npm build:css → Docker image → GitHub Actions → Terraform → Cloud Run
platform-architectuur · live
JARIDATA.DEV · PORTFOLIOPLATFORM · LIVE CONTENT-INPUTS EDGE-LAAG CLOUD RUN RUNTIME FRONTEND CLIENT-OUTPUT GCP CLOUD RUN CONTAINER content-rendering inputs styling & interactie infrastructuur-provisioning & edge-regels cases.py portfolio-records i18n.py tweetalige copy-definities Jinja2 templates macros & paginastructuren SVG partials interactieve visualisaties bezoeker / crawler HTTP GET request Cloudflare Edge WAF · CDN · origin shield FastAPI Core (pages.py) SEO-metadata & responseheaders Jinja2 SSR Engine gelokaliseerde paginacompilatie Router & Middleware CSP-nonces · origin-guard tokens.css dark / light tokens case.css visueel stijlsysteem vanilla JS thema- & interactielopen static assets immutable cache + ?v= bezoekerbrowser HTML + SEO / sitemap GitHub Actions ruff · mypy · pytest · css Docker build multi-stage runtime-image GCP Artifact Registry container-release-deploy Terraform IaC GCP + Cloudflare inrichting // Bezoekersverzoeken lopen via Cloudflare Edge naar de Cloud Run FastAPI-app, waar content-driven Jinja SSR gelokaliseerde pagina's met CSS-tokens compileert; GitHub Actions en Terraform automatiseren de levering
volledige visual openen ↗

FastAPI levert de routes, Jinja2 rendert HTML op de server en de contentlaag voedt templates met expliciete NL/EN-records. Static assets worden met een versie-query geladen en immutable gecachet. De Cloud Run-infrastructuur staat in Terraform; GitHub Actions draait eerst kwaliteitspoorten voordat de image gebouwd en uitgerold wordt.

Applicatielaag

De route-opbouw staat centraal in app/api/routers/pages.py. Eén router-factory bouwt de Nederlandse canonical routes en de Engelse /en/*-routes, zodat handlers niet per taal kunnen divergeren.

Deep-dive cases worden vanuit app/content/cases.py bepaald. Als deep_dive=True, verwacht de template bewust twee includes: <slug>.nl.html en <slug>.en.html. Er is geen stille fallback naar Engels; ontbrekende content moet hard falen.

SEO is onderdeel van dezelfde applicatielaag. template_response() injecteert canonical en hreflang-data, terwijl meta.py robots.txt, sitemap.xml en /healthz serveert.

De app is klein, maar het routingcontract is serieus.

De site heeft geen losse locale-sites, geen client-side taalwissel en geen sitemap die met de hand wordt bijgewerkt. Routes, cases en SEO-metadata komen uit dezelfde brondata.

Frontend- en designsysteem

De frontend is bewust zonder client-framework gebouwd. Jinja levert de HTML; JavaScript voegt interactie toe waar dat waarde heeft: theme-toggle, mobiele navigatie, portfolio peek-panel, terminalgedrag, reveal-effecten en case-lightboxes.

app/static/css/src/tokens.css is de tokenlaag. Dark mode gebruikt de Retro Developer Terminal-richting; light mode volgt de Polar Blueprint-richting. Die twee thema's hoeven niet dezelfde esthetiek te delen, maar ze moeten wel allebei vanuit tokens en expliciete light-mode selectors worden aangestuurd.

Casepagina's en architecture visuals gebruiken bestaande classes zoals .case-main--cat-* en .case-archviz. Daardoor blijven accenten, lightbox-gedrag, reduced-motion en light/dark overrides in het bestaande CSS-contract.

Contentarchitectuur

app/content/cases.py is de single source of truth voor portfolio-cards: slug, categorie, bento-formaat, titel, blurb, TL;DR, stack, metrics, deep-dive status en architecture-flag. Localevelden zijn dictionaries met verplichte nl en en waarden.

Korte chrome-copy staat in app/content/i18n.py. Lange case-tekst leeft in Jinja-includes per locale. Tests controleren dat deep-dive includes bestaan, dat sectie-aantallen gelijk blijven en dat macrogebruik tussen NL en EN niet ongemerkt uit elkaar loopt.

Nieuwe cases schalen daardoor zonder nieuwe routecode: datarecord toevoegen, twee locale-includes schrijven en optioneel een SVG-partial koppelen voor de architecture view.

Deployment & operations

De Dockerfile heeft een Node-builderfase voor CSS en image-optimalisatie, gevolgd door een Python-runtime met uv. De runtime start Uvicorn op poort 8080, passend bij Cloud Run.

Terraform beheert de Cloud Run-service, service account, Cloudflare-resources en monitoringconfiguratie. De Cloud Run-module zet health probes, min_instance_count=0, max_instance_count=1, request-concurrency en environment variables zoals APP_VERSION, CLOUD_REGION en CLOUDFLARE_ONLY.

GitHub Actions voert een infra-plan, lint, format-check, mypy en coverage-tests uit voordat de deploy-job bouwt, pusht en een Terraform plan/apply uitvoert. De post-deploy smoke test verwacht bewust een 403 op de directe run.app-URL, zodat origin-bypass via CloudflareOnly fail-closed blijft.

AI-agent guardrails

De repository bevat expliciete regels voor AI-assisted development. AGENTS.md, DESIGN.md, CLAUDE.md, GEMINI.md en app/static/README.md leggen vast hoe agents moeten werken met privacy, i18n, CSS-tokens, theme-splitsing, frontend-validatie en audit-output.

Voor theme- en frontendwerk is de trace verplicht: tokens → CSS-consumers → templates → Python-content → JavaScript runtime styles → SVG-classes → routes → browservalidatie. Dat is bewust streng, omdat kleine kleur- of specificity-fixes anders makkelijk dark mode, light mode of CSP breken.

Documentatie is hier geen bijlage maar een releaseguard.

De docs beschrijven niet alleen intentie. Ze leggen concrete verboden en checks vast, zoals geen directe environment reads in app/, nonce op inline scripts/styles, geen inline style-attributen voor CSP-gevoelige elementen en IaC-first voor infrastructuur.

Engineeringbeslissingen die ertoe doen

  1. SSR boven SPA. De site is content- en SEO-zwaar; FastAPI/Jinja2 levert daarvoor minder runtime-complexiteit dan een client-heavy app.
  2. Vanilla JS waar het genoeg is. Theme, navigatie, terminal en case-interactie hebben geen framework nodig.
  3. Tokens als contract. Kleur en surface-gedrag horen in CSS-variabelen, niet verspreid door templates of JavaScript.
  4. Content los van rendering. Portfolio metadata leeft in Python-data; long-form copy leeft in locale-includes.
  5. Asymmetrische thema's. Dark en light mode delen componenten, maar niet blind dezelfde esthetiek.
  6. IaC en CI als basis. De persoonlijke site gebruikt dezelfde reviewbare build- en deploygewoontes als productieplatformen.

Resultaat

jaridata.dev bewijst vooral werkwijze. De site laat zien dat ik niet alleen data/cloud/AI-architectuur beschrijf, maar ook mijn eigen publieke platform met dezelfde discipline bouw.

  • server-rendered FastAPI/Jinja2 applicatie
  • NL/EN routes met canonical en hreflang
  • content-gedreven portfolio-cases
  • token-based dark/light designsysteem
  • CSP, security headers en Cloudflare-only origin protection
  • immutable static caching met versie-query's
  • Docker- en Cloud Run-deploymentpad
  • Terraform, GitHub Actions en testbare guardrails

Status

De huidige site is live als productie-achtige portfolio-app. De code bevat routes, templates, i18n, SEO, middleware, tests, Docker, Terraform en CI/CD-context. Tegelijk zijn sommige commerciële functionaliteiten bewust nog niet actief: de contactpagina is een placeholder en functionele data/AI API-endpoints staan volgens de README op de roadmap, niet in de huidige app.