codexmachina
App schema

Fitness tracker, built on every verified stack.

Fitness tracker: user-owned workout plans, a shared exercise catalog, append-only workout logs, and per-set reps/weight rows.

40 verified stacks40 greenLast updated 2026-08-23How we verify →

40 verified stacks

Showing 12 across 1 app types. Every stack page links its neighbours along each axis, so any combination is a click or two from here.

Next.js 16 (App Router) + MySQL 8Auth.js (NextAuth) · Resend
Next.js 16 (App Router) + MySQL 8Better Auth
Next.js 16 (App Router) + MySQL 8Clerk · Resend
Next.js 16 (App Router) + MySQL 8Supabase Auth
Next.js 16 (App Router) + Postgres (Neon)Auth.js (NextAuth)
Next.js 16 (App Router) + Postgres (Neon)Better Auth · Resend
Next.js 16 (App Router) + Postgres (Neon)Supabase Auth · Resend
Nuxt 4 + MySQL 8Supabase Auth · Resend
Nuxt 4 + Postgres (Neon)Better Auth · Resend
Nuxt 4 + Postgres (Neon)Clerk
React Router v8 + MySQL 8Better Auth · Resend
React Router v8 + MySQL 8Supabase Auth

What Fitness tracker gives you

Three of the four tables are user-scoped and one deliberately is not. workout_plans and workout_logs both foreign-key the auth fragment's user, and log_sets inherits that scoping through its parent log, but exercises is a shared catalog: no owner column, name carrying a unique constraint. A back squat is one row for the whole install, and every set anyone logs points at that same id, which is what makes cross-user aggregates possible at all. It is also the constraint you inherit — a private custom exercise needs a new column or a new table, because the catalog namespace is global.

log_sets is where the volume lives: one row per set, with set_number, reps, and a nullable weight_kg held as exact numeric rather than a float, so 2.5 kg plate increments accumulate without drift and a bodyweight set simply leaves the column null. Its exercise_id reference is the one FK in the schema with no ON DELETE clause, so the catalog is protected by default: the database refuses to delete an exercise any logged set still cites, and history cannot be hollowed out by a catalog cleanup. performed_at on workout_logs has no defaultNow, and that is the point — the column records when the session happened, not when the row was written, so entering Tuesday's workout on Thursday is the normal path rather than a correction.

idx_log_user_time on (user_id, performed_at) turns a month of training into a range scan already in date order. plan_id is nullable and ON DELETE SET NULL: an unplanned session is a valid log, and deleting a plan detaches its history instead of erasing it, which is the exact counterpart to user_id cascading and taking plans, logs and sets with it. The gap to plan around is per-exercise history. log_sets is indexed on log_id only, so the query behind a personal-record chart — every bench press this athlete has ever pressed — walks the user's logs through idx_log_user_time first and joins sets from there.

Built for

A month of one athlete's training, in date order

idx_log_user_time on workout_logs (user_id, performed_at) range-scans the window and returns it already ordered, so a calendar or streak view needs no sort.

Every set performed in one session

idx_set_log on log_sets.log_id pulls the session's rows; set_number, reps and weight_kg carry the detail, and the sets cascade away with their log.

The plans an athlete has built

idx_plan_owner on workout_plans.owner_id lists a user's plans, and owner_id cascades from user so no plan outlives its account.

Logging a workout that follows no plan

workout_logs.plan_id is nullable with ON DELETE SET NULL, so an ad-hoc session is a first-class log and retiring a plan leaves its past sessions intact with a null plan_id.

One canonical exercise across everybody's history

exercises.name is unique with no owner column, and log_sets.exercise_id references it without an ON DELETE clause — a referenced exercise cannot be deleted, so the id every set points at stays valid.

Why Fitness tracker, specifically

note

exercises.name carries a unique constraint — the exercise catalog is shared across all users, so duplicate names are a schema error, not a soft collision.

note

workout_logs.plan_id is set null on plan deletion, not cascaded — historical logs are preserved even when the originating plan is removed.