Database Schema & Migrations

Last updated: 2026-07-08

This document covers the database structure, schema design, and migration process for SparkyFitness.

Note:

  • For authoritative table definitions, see db_schema_backup.sql (SQL source) or shared/src/schemas/database/ (TypeScript Zod schemas).
  • For security tier classifications and permission mappings, see 11.database-security-tiers.md.

Database Overview

SparkyFitness uses PostgreSQL 15+ with Row Level Security (RLS) to ensure data isolation between users. The database follows a normalized design with clear relationships between entities.


Schema Design Principles

Naming Conventions

  • Tables: Snake case, plural (e.g., food_entries, user_preferences)
  • Columns: Snake case (e.g., created_at, user_id, total_calories)
  • Foreign Keys: {table_name}_id format (e.g., user_id, food_item_id)
  • Indexes: Descriptive names (e.g., idx_food_entries_user_date, idx_measurements_user_type)

Standard Fields

All tables include these audit fields:

  • id - UUID primary key using gen_random_uuid()
  • created_at - Timestamp with timezone, defaults to NOW()
  • updated_at - Timestamp with timezone, updated via triggers

Row Level Security (RLS)

Every user-specific table has RLS policies to enforce data isolation. PostgreSQL context functions (initialized via public.set_app_context(userId, authenticatedUserId) on every connection) control access:

-- Example: diary logs are readable by delegates with diary read access and
-- writable by delegates with can_manage_diary. rls_policies.sql usually applies
-- this via the create_diary_policy('food_entries') generator, which emits:
ALTER TABLE food_entries ENABLE ROW LEVEL SECURITY;
CREATE POLICY select_policy ON public.food_entries FOR SELECT TO PUBLIC
  USING (has_diary_read_access(user_id));
CREATE POLICY modify_policy ON public.food_entries FOR ALL TO PUBLIC
  USING (has_diary_access(user_id))
  WITH CHECK (has_diary_access(user_id));

Key functions:

  • current_user_id() — active profile context (may differ during family delegation; from app.user_id)
  • authenticated_user_id() — the true logged-in actor (never changes; from app.authenticated_user_id)
  • can_access_user_data(target_user_id, permission_type, authenticated_user_id()) — resolves a logical permission (diary, checkin, medications, reports, plus *_read variants) against the family_access grant. Domain shortcuts wrap it: has_diary_read_access, has_diary_access, has_checkin_read_access, has_medication_access, has_family_access.

Cycle and pregnancy tables are owner-only (no delegation). See SparkyFitnessServer/db/rls_policies.sql for the complete policy set and the create_*_policy generators.


Table Index

Quick reference of all tables by domain and purpose. For detailed security tier, permission type, and access rules, see 11.database-security-tiers.md.

Authentication & Identity (Tier 1: Owner-Only)

TablePurpose
userAccount identity, password hash, email
sessionActive authentication sessions
api_keyUser-generated API keys for external access
passkeyPasswordless login credentials
two_factor2FA recovery codes and secrets
verificationEmail verification tokens
accountAuth credentials and email accounts

Food & Nutrition (Tier 2/3: Owner-Write, Delegate-Read/Write)

TablePurpose
foodsCustom food items created by user
food_variantsServing size options for foods
food_entriesLogged meals/calories for the day
food_entry_mealsMeal details associated with logged entries
mealsCustom meal templates
meal_foodsIngredients assigned to meals
meal_typesCustom meal type definitions (breakfast, lunch, etc.)
meal_plansWeekly meal planning schedules
meal_plan_templatesReusable meal plan templates (supports multiple active plans per user)
meal_plan_template_assignmentsScheduled meal templates to calendar
meal_plan_assignment_setsSets within assigned meal plans

Exercise & Workouts (Tier 2/3: Owner-Write, Delegate-Read/Write)

TablePurpose
exercisesCustom exercises created by user
exercise_entriesLogged exercises for the day
exercise_preset_entriesLogged workout presets for the day
exercise_entry_setsReps, weights, and sets completed
exercise_entry_activity_detailsHeart rate, distance, activity data
workout_presetsCustom workout/preset templates
workout_preset_exercisesExercises assigned to presets
workout_preset_exercise_setsReps/sets configured in presets
workout_plan_templatesTemplates for weekly workout schedules
workout_plan_template_assignmentsScheduled workout templates to calendar
workout_plan_assignment_setsSets within assigned workout plans

Measurements & Health (Tier 1/3: Owner-Only or Delegate-Write)

TablePurpose
check_in_measurementsWeight, neck, waist, hips measurements
check_in_photosProgress photos
custom_measurementsUser-defined custom measurement types
custom_categoriesUser-defined measurement categories
water_intakeTotal water logged for the day
water_intake_entriesIndividual logged water cups
water_containersConfigured container sizes
sleep_entriesSleep logs (bedtime, wake time)
sleep_entry_stagesSleep stage breakdowns (REM, Deep, Light)
sleep_need_calculationsAI sleep need calculations
daily_sleep_needSleep goals calculated for the day

Fasting, Mood, Medications, & Symptoms (Tier 1/3: Owner-Only or Delegate-Write)

TablePurpose
fasting_logsFasting timeline logs (start/end fast)
mood_entriesLogged mood and energy levels
user_custom_moodsUser-defined mood tags (icon/color)
medicationsCustom medication inventory lists
medication_schedulesReminders and schedules for medications
medication_entriesLogs of medications taken
medication_pensTrackers for medication delivery pens
medication_titration_stepsAutomated titration dosage plans
injection_entriesInjection logs (site, time, etc.)
user_custom_symptomsCustom tracked health symptoms
symptom_entriesLogs of daily tracked symptom severity

Cycle & Pregnancy (Tier 1: Owner-Only)

TablePurpose
cyclesMenstrual cycle history records
cycle_settingsCycle hub settings (mode, parameters)
cycle_daily_entriesPer-day cycle logs (flow, BBT, mood, etc.)
cycle_test_entriesOvulation and pregnancy test logs
pregnanciesPregnancy records (due date, status)
pregnancy_kick_sessionsFetal kick-counter sessions
pregnancy_contractionsContraction timer logs
pregnancy_photosBump photo journal
pregnancy_checklist_stateWeekly pregnancy checklist completion
health_appointmentsPrenatal and other health appointments

User Preferences & Settings (Tier 2: Owner-Only Write, Delegate-Read)

TablePurpose
profilesUser full name, height, display metrics
user_preferencesUnit display preferences (Metric vs Imperial)
user_nutrient_display_preferencesNutrient column display preferences
user_meal_visibilitiesVisibility settings for meals
user_goalsActive daily calorie/macro goals
user_custom_nutrientsCustom nutrient definitions
user_nutrient_goal_preferencesPer-user minimum/maximum/target goal direction override per nutrient (predefined or custom)
user_water_containersConfigured container sizes
user_dashboard_layoutsRearranged dashboard widget positions
user_medication_display_preferencesGLP-1/Medication display preferences
user_mood_display_preferencesMood picker visibility settings
user_cycle_display_preferencesCycle dashboard tile visibility
user_allergen_preferencesAllergen preferences
user_ignored_updatesRecords of skipped release updates

AI & Chat (Tier 1: Owner-Only)

TablePurpose
sparky_chat_historyAI Assistant chat messages and history
ai_service_settingsUser-defined custom assistant configurations

Admin & System (Tier 1: Admin-Only or Public)

TablePurpose
global_settingsApplication feature flags and config
sso_providerActive Single Sign-On providers
oidc_providersOpenID Connect integration settings
external_provider_typesSearch provider configurations (FatSecret, USDA)
external_data_providersConfigured API integrations
medication_typesMedication categories lookup
medication_route_typesMedication administration routes lookup
medication_schedule_typesMedication scheduling frequencies lookup
admin_activity_logsAdmin action audits
day_classification_cacheDaily summary caching logs

Internal & Shared (Tier 2: Owner-Only or System)

TablePurpose
onboarding_dataInitial user onboarding metrics
onboarding_statusUser onboarding status
family_accessSharing rules and delegation credentials
backup_settingsAutomated database backup settings

Database Migrations

SparkyFitness uses a custom migration system that runs automatically on server startup.

Migration Process

The migration system:

  1. Checks current database version on startup
  2. Applies pending migrations in order
  3. Tracks applied migrations in the migrations table
  4. Logs migration results for debugging

Migration Structure

Migrations are stored in SparkyFitnessServer/db/migrations/ with the naming pattern:

YYYYMMDDHHMMSS_description.sql

Example: 20240315103000_add_exercise_tracking.sql

Creating a New Migration

  1. Create the migration file in the migrations directory:
    cd SparkyFitnessServer/db/migrations/
    touch 20240315142000_add_meal_planning.sql
    
  2. Write the migration SQL — see the migration checklist in the repo's agent-docs folder (for developers and AI tools)
  3. Update db_schema_backup.sql with the new schema state
  4. Apply RLS policies (these are reapplied automatically on startup from db/rls_policies.sql)

Migration Best Practices

Backwards Compatibility

  • Add columns with default values to avoid breaking existing code
  • Create new tables rather than modifying existing ones when possible
  • Use transactions to ensure atomic migrations
  • Test migrations on development data first

Transaction Management

BEGIN;
-- All migration statements here
-- If any statement fails, entire migration rolls back
COMMIT;

Index Creation

-- Create indexes concurrently to avoid blocking
CREATE INDEX CONCURRENTLY idx_food_entries_user_date 
ON food_entries(user_id, created_at);

Data Migration

BEGIN;
-- Create new table
CREATE TABLE new_table (id UUID PRIMARY KEY, ...);

-- Migrate existing data
INSERT INTO new_table (...)
SELECT ... FROM old_table;

-- Drop old table (after verifying migration)
-- DROP TABLE old_table;

COMMIT;

Rollback Strategy

While not automated, rollback migrations can be created:

  1. Document rollback steps in migration comments
  2. Create rollback scripts for complex migrations
  3. Test rollback procedures in development
  4. Backup database before applying production migrations

Troubleshooting Migrations

Migration Fails

  1. Check migration logs in application startup
  2. Verify database connection and permissions
  3. Check for syntax errors in migration SQL
  4. Ensure migration dependencies are met

Migration Tracking Issues

-- Check applied migrations
SELECT * FROM migrations ORDER BY applied_at;

-- Manually mark migration as applied (if needed)
INSERT INTO migrations (version, applied_at) 
VALUES ('20240315142000', NOW());

Database State Issues

-- Check table structure
\d table_name

-- Check RLS policies
SELECT * FROM pg_policies WHERE tablename = 'table_name';

-- Check indexes
SELECT indexname, indexdef FROM pg_indexes 
WHERE tablename = 'table_name';

Database Maintenance

Performance Monitoring

  • Query performance: Use EXPLAIN ANALYZE for slow queries
  • Index usage: Monitor index usage with pg_stat_user_indexes
  • Connection monitoring: Track connection pool usage

Regular Maintenance

  • VACUUM: Regular vacuuming for performance
  • ANALYZE: Update table statistics
  • Index maintenance: Rebuild indexes if needed
  • Log rotation: Rotate and archive database logs