System design
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.