About this build

A real tool I use daily, engineered as a portfolio piece.

Why I built it

I walk on a treadmill at the gym and outdoors. That data used to live in two disconnected places — my head (speed/incline/duration) and Apple Health (passive steps, distance, outdoor walks) — so I had no long-term view of total movement and no record to look back on a year from now.

Olympic reconciles both sources into one dashboard: daily, weekly, monthly, and a year-long heatmap. It stays a ten-second logging tool on my phone while doubling as something a hiring manager can open in two minutes with no login.

How the data flows

Apple Health exportone-time backfill (zip)Health Auto Exportnightly JSON (iOS app)Treadmill entryPWA form (owner)/api/healthimport · ingest/api/workoutsPOST (owner)Neon Postgresdaily_metric (phone totals)workout (treadmill + outdoor)read-time dedup →reconciled daily totals

Apple Health arrives two ways — a one-time backfill zip and a nightly push from the Health Auto Export app — and both land in Postgres as phone-reported daily metrics plus outdoor walks. Treadmill sessions are logged directly. Because the phone already counts treadmill steps, displayed totals subtract each treadmill workout's own steps/distance/calories from the phone total for that day, then add the treadmill figures back — so a workout is counted once, computed fresh on every read.

Decisions & tradeoffs

Read-time dedup over precomputed totals

Phone and treadmill both count the same steps. Rather than maintaining a reconciled total on every write, the overlap is subtracted at read time — one rule, no drift, and re-importing Apple Health is always safe.

Passkeys over passwords

One human, several devices, no password to phish or leak. A signed httpOnly cookie carries the session; credentials live in Postgres. Registration is gated by a one-time bootstrap secret so a public /register can't be abused.

Route-level access control over deployment protection

v1 hid everything behind Vercel SSO. v2 needs a public dashboard and a private write side from one deployment, so a single requireOwner() guard wraps every mutation and secret-bearing route, and public reads go through field-whitelist DTOs.

Single-user, no multi-tenancy

No user_id columns, no sign-up, no org model. The public audience is anonymous and read-only; the owner is me. That keeps the schema and the surface area small.

PWA over native

The daily job is logging a workout one-handed at the gym. A home-screen PWA with an offline save queue does that without an app store, and installs for any public visitor too.

Stack & live metrics

31
workouts logged
1,788
days of data
0
day streak
254
automated tests

Figures above are queried live at render (tests captured at build). Lighthouse on production: Accessibility 100, Best Practices 100, SEO 100.

Framework
Next.js 16 App Router (TypeScript)
UI
shadcn/ui + Tailwind CSS
Charts
Recharts + react-activity-calendar
Database
Neon Postgres (Vercel Marketplace)
ORM
Drizzle
Auth
WebAuthn passkeys + jose-signed session cookie
Hosting
Vercel (Fluid Compute, Node.js)