How this site works

This portfolio is intentionally a working system, not a static page. Here is the request flow and the decisions behind each layer.

Browser
The visitor's device
HTTPS
nginx
Reverse proxy · TLS · security headers · www → apex
Next.js — web
SSR · React · renders from the API over HTTP/JSON
Fastify — API
Zod · OpenAPI · rate-limit · /health · /metrics
SQL · Drizzle
PostgreSQL
Projects · contact messages · analytics

Web - Next.js (App Router)

  • Server Components fetch from the API at request time, so pages render from live data.
  • Tailwind for styling; ships as a minimal standalone server in Docker.
  • The contact form POSTs directly to the API and shows real success/error states.

API - Fastify + TypeScript

  • Zod schemas are the single source of truth: they validate input, serialize output, and generate the OpenAPI docs.
  • Rate limiting, Helmet security headers and CORS are configured per environment.
  • Structured JSON logging (pino) with a request id on every log line.
  • Prometheus metrics at /metrics and liveness/readiness probes at /health and /ready.

Data - Postgres via Drizzle ORM

  • Type-safe, SQL-first schema and queries - no hidden magic, migrations are plain SQL in version control.
  • Projects, contact messages and page-view analytics each get a purpose-built table and indexes.
  • Aggregates (visitor stats) are computed in SQL, not in application code.

Infra - Docker + CI/CD

  • Multi-stage Dockerfiles produce small production images for both apps.
  • docker-compose runs API, web and Postgres together; an nginx reverse proxy terminates TLS.
  • GitHub Actions runs typecheck, tests (against a real Postgres) and builds on every push.
  • Deployable to any Docker host - DigitalOcean, Hetzner, Fly, or bare metal - with no code changes.

See it for yourself

The API is self-documenting. Every endpoint below is live and interactive.