SparkyFitness Database Schema Documentation
Overview
The database uses PostgreSQL with Row Level Security (RLS) policies to ensure data privacy and proper access control. All tables are designed with audit fields (created_at, updated_at) and use UUIDs for primary keys.
Core Tables
1. profiles
Purpose: Store user profile information
-- profiles table
create table public.profiles (
id uuid not null default gen_random_uuid (),
email text,
full_name text,
phone text,
bio text,
date_of_birth date,
avatar_url text,
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint profiles_pkey primary key (id),
constraint profiles_id_fkey foreign KEY (id) references auth.users (id) on delete CASCADE
) TABLESPACE pg_default;
RLS: Users can only access their own profile Relationships: Links to all user-specific tables
2. user_goals
Purpose: Track user nutrition and fitness goals over time
-- user_goals table
create table public.user_goals (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
goal_date date null, -- Null for default goal
calories numeric null,
protein numeric null,
carbs numeric null,
fat numeric null,
water_goal integer null,
created_at timestamp with time zone null default now(),
updated_at timestamp with time zone null default now(),
saturated_fat numeric null, -- Added for comprehensive nutrition goals
polyunsaturated_fat numeric null,
monounsaturated_fat numeric null,
trans_fat numeric null,
cholesterol numeric null,
sodium numeric null,
potassium numeric null,
dietary_fiber numeric null,
sugars numeric null,
vitamin_a numeric null,
vitamin_c numeric null,
calcium numeric null,
iron numeric null,
constraint user_goals_pkey primary key (id),
constraint user_goals_user_id_fkey foreign KEY (user_id) references auth.users (id),
constraint user_goals_unique_user_date unique (user_id, goal_date) -- Ensure only one goal per user per date
) TABLESPACE pg_default;
RLS: Users can only access their own goals Special Features:
- NULL goal_date represents default goals
- Function
manage_goal_timeline()
handles goal updates - Function
get_goals_for_date()
retrieves goals for specific dates
3. user_preferences
Purpose: Store user interface and application preferences
-- user_preferences table
create table public.user_preferences (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
date_format character varying(50) not null default 'yyyy-MM-dd',
default_weight_unit character varying(50) not null default 'kg',
default_measurement_unit character varying(50) not null default 'cm',
auto_clear_history text null default 'never', -- 'never', 'session', '7days', '30days'
system_prompt text null, -- User-specific override for AI system prompt
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint user_preferences_pkey primary key (id),
constraint user_preferences_user_id_fkey foreign KEY (user_id) references auth.users (id) on delete CASCADE,
constraint user_preferences_auto_clear_history_check check (
(
auto_clear_history = any (array['never'::text, 'session'::text, '7days'::text, '30days'::text])
)
)
) TABLESPACE pg_default;
RLS: Users can only access their own preferences Auto-Creation: Trigger creates preferences on user registration
Food & Nutrition Tables
4. foods
Purpose: Master food database with nutrition information
-- foods table
create table public.foods (
id uuid not null default gen_random_uuid (),
name character varying(255) not null,
calories numeric null,
protein numeric null,
carbs numeric null,
fat numeric null,
serving_size numeric null, -- Default serving size in grams or standard unit
serving_unit character varying(50) null, -- e.g., 'g', 'ml', 'piece'
is_custom boolean null default false, -- True if created by a user
user_id uuid null, -- Creator user_id if is_custom is true
created_at timestamp with time zone null default now(),
updated_at timestamp with time zone null default now(),
barcode character varying(255) null, -- For scanning
openfoodfacts_id character varying(255) null, -- Link to external databases
shared_with_public boolean null default false,
default_variant_id uuid null, -- Reference to the default food_variant for this food
saturated_fat numeric null, -- Added for comprehensive nutrition
polyunsaturated_fat numeric null,
monounsaturated_unsaturated numeric null,
trans_fat numeric null,
cholesterol numeric null,
sodium numeric null,
potassium numeric null,
dietary_fiber numeric null,
sugars numeric null,
vitamin_a numeric null,
vitamin_c numeric null,
calcium numeric null,
iron numeric null,
constraint foods_pkey primary key (id),
constraint foods_user_id_fkey foreign KEY (user_id) references auth.users (id),
constraint foods_default_variant_id_fkey foreign KEY (default_variant_id) references food_variants (id)
) TABLESPACE pg_default;
RLS: Users can access public foods and their own custom foods Special Features:
- Public foods (user_id is NULL) accessible to all
- Custom foods linked to specific users
- Comprehensive nutrition profile
5. food_variants
Purpose: Alternative serving sizes and preparations for foods
-- food_variants table
create table public.food_variants (
id uuid not null default gen_random_uuid (),
food_id uuid not null,
serving_size numeric not null,
serving_unit character varying(50) not null,
calories numeric null,
protein numeric null,
carbs numeric null,
fat numeric null,
saturated_fat numeric null,
polyunsaturated_fat numeric null,
monounsaturated_unsaturated numeric null,
trans_fat numeric null,
cholesterol numeric null,
sodium numeric null,
potassium numeric null,
dietary_fiber numeric null,
sugars numeric null,
vitamin_a numeric null,
vitamin_c numeric null,
calcium numeric null,
iron numeric null,
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint food_variants_pkey primary key (id),
constraint food_variants_food_id_fkey foreign KEY (food_id) references foods (id)
) TABLESPACE pg_default;
RLS: Inherits access from parent food Use Case: Different serving sizes (1 cup, 1 slice, etc.)
6. food_entries
Purpose: Daily food consumption log
-- food_entries table
create table public.food_entries (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
food_id uuid not null,
meal_type character varying(50) not null, -- e.g., 'breakfast', 'lunch', 'dinner', 'snack'
quantity numeric not null,
unit character varying(50) null, -- e.g., 'g', 'oz', 'piece'
entry_date date not null,
created_at timestamp with time zone not null default now(),
variant_id uuid null, -- Link to food_variants if applicable
constraint food_entries_pkey primary key (id),
constraint food_entries_food_id_fkey foreign KEY (food_id) references foods (id),
constraint food_entries_user_id_fkey foreign KEY (user_id) references auth.users (id),
constraint food_entries_variant_id_fkey foreign KEY (variant_id) references food_variants (id)
) TABLESPACE pg_default;
RLS: Users can only access their own food entries Special Features:
- Links to either base food or specific variant
- Flexible quantity and unit system
Meal Management Tables
7. meals
Purpose: Store user-defined meal templates
-- meals table
CREATE TABLE public.meals (
id uuid NOT NULL DEFAULT gen_random_uuid(),
user_id uuid NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
is_public BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT meals_pkey PRIMARY KEY (id),
CONSTRAINT meals_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE
);
RLS: Handled at application layer. Users can view their own meals and public meals. Users can insert, update, and delete their own meals. Special Features:
- Allows users to create reusable meal templates.
is_public
flag for sharing meals.
8. meal_foods
Purpose: Link foods to meal templates
-- meal_foods table
CREATE TABLE public.meal_foods (
id uuid NOT NULL DEFAULT gen_random_uuid(),
meal_id uuid NOT NULL,
food_id uuid NOT NULL,
variant_id uuid,
quantity NUMERIC NOT NULL,
unit VARCHAR(50) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT meal_foods_pkey PRIMARY KEY (id),
CONSTRAINT meal_foods_meal_id_fkey FOREIGN KEY (meal_id) REFERENCES public.meals (id) ON DELETE CASCADE,
CONSTRAINT meal_foods_food_id_fkey FOREIGN KEY (food_id) REFERENCES public.foods (id) ON DELETE CASCADE,
CONSTRAINT meal_foods_variant_id_fkey FOREIGN KEY (variant_id) REFERENCES public.food_variants (id) ON DELETE SET NULL
);
RLS: Handled at application layer. Inherits access from parent meal.
Relationships: Links to meals
, foods
, and food_variants
.
9. meal_plans
Purpose: Schedule meals or individual foods for specific dates
-- meal_plans table
CREATE TABLE public.meal_plans (
id uuid NOT NULL DEFAULT gen_random_uuid(),
user_id uuid NOT NULL,
meal_id uuid,
food_id uuid,
variant_id uuid,
quantity NUMERIC,
unit VARCHAR(50),
plan_date DATE NOT NULL,
meal_type VARCHAR(50) NOT NULL,
is_template BOOLEAN DEFAULT FALSE,
template_name VARCHAR(255),
day_of_week INTEGER,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT meal_plans_pkey PRIMARY KEY (id),
CONSTRAINT chk_meal_or_food CHECK (
(meal_id IS NOT NULL AND food_id IS NULL AND variant_id IS NULL AND quantity IS NULL AND unit IS NULL) OR
(meal_id IS NULL AND food_id IS NOT NULL AND variant_id IS NOT NULL AND quantity IS NOT NULL AND unit IS NOT NULL)
),
CONSTRAINT meal_plans_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE,
CONSTRAINT meal_plans_meal_id_fkey FOREIGN KEY (meal_id) REFERENCES public.meals (id) ON DELETE CASCADE,
CONSTRAINT meal_plans_food_id_fkey FOREIGN KEY (food_id) REFERENCES public.foods (id) ON DELETE CASCADE,
CONSTRAINT meal_plans_variant_id_fkey FOREIGN KEY (variant_id) REFERENCES public.food_variants (id) ON DELETE SET NULL
);
RLS: Handled at application layer. Users can view, insert, update, and delete their own meal plans. Special Features:
- Can link to a
meal
template or directly to afood
item. is_template
andtemplate_name
for creating reusable meal plan segments.day_of_week
for recurring schedules.
10. meal_plan_templates
Purpose: Store user-defined meal plan templates that span multiple days or weeks.
-- meal_plan_templates table
CREATE TABLE public.meal_plan_templates (
id uuid NOT NULL DEFAULT gen_random_uuid(),
user_id uuid NOT NULL,
plan_name VARCHAR(255) NOT NULL,
description TEXT,
start_date DATE NOT NULL,
end_date DATE,
is_active BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT meal_plan_templates_pkey PRIMARY KEY (id),
CONSTRAINT meal_plan_templates_user_id_fkey FOREIGN KEY (user_id) REFERENCES auth.users (id) ON DELETE CASCADE
);
RLS: Handled at the application layer. Special Features:
is_active
flag to indicate the currently active meal plan.start_date
andend_date
for defining the duration of the plan.
11. meal_plan_template_assignments
Purpose: Link meals to specific days and meal types within a meal plan template.
-- meal_plan_template_assignments table
CREATE TABLE public.meal_plan_template_assignments (
id uuid NOT NULL DEFAULT gen_random_uuid(),
template_id uuid NOT NULL,
day_of_week INTEGER NOT NULL,
meal_type VARCHAR(50) NOT NULL,
meal_id uuid NOT NULL,
CONSTRAINT meal_plan_template_assignments_pkey PRIMARY KEY (id),
CONSTRAINT meal_plan_template_assignments_template_id_fkey FOREIGN KEY (template_id) REFERENCES public.meal_plan_templates (id) ON DELETE CASCADE,
CONSTRAINT meal_plan_template_assignments_meal_id_fkey FOREIGN KEY (meal_id) REFERENCES public.meals (id) ON DELETE CASCADE
);
RLS: Handled at the application layer.
Relationships: Links meal_plan_templates
to meals
.
12. food_entries
Purpose: Daily food consumption log
-- food_entries table
create table public.food_entries (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
food_id uuid not null,
meal_type character varying(50) not null, -- e.g., 'breakfast', 'lunch', 'dinner', 'snack'
quantity numeric not null,
unit character varying(50) null, -- e.g., 'g', 'oz', 'piece'
entry_date date not null,
created_at timestamp with time zone not null default now(),
variant_id uuid null, -- Link to food_variants if applicable
meal_plan_template_id uuid null, -- Link to the meal plan template
constraint food_entries_pkey primary key (id),
constraint food_entries_food_id_fkey foreign KEY (food_id) references foods (id),
constraint food_entries_user_id_fkey foreign KEY (user_id) references auth.users (id),
constraint food_entries_variant_id_fkey foreign KEY (variant_id) references food_variants (id),
constraint food_entries_meal_plan_template_id_fkey foreign KEY (meal_plan_template_id) references meal_plan_templates (id) on delete set null
) TABLESPACE pg_default;
RLS: Users can only access their own food entries Special Features:
- Links to either base food or specific variant
- Flexible quantity and unit system
- Can be linked to a meal plan template
Measurement & Tracking Tables
10. check_in_measurements
Purpose: Standard body measurements tracking
-- check_in_measurements table
create table public.check_in_measurements (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
entry_date date not null default CURRENT_DATE,
weight numeric null,
neck numeric null,
waist numeric null,
hips numeric null,
steps integer null,
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint check_in_measurements_pkey primary key (id),
constraint check_in_measurements_user_date_unique unique (user_id, entry_date)
) TABLESPACE pg_default;
RLS: Users can only access their own measurements Use Case: Standard body metrics tracking
11. custom_categories
Purpose: User-defined measurement categories
-- custom_categories table
create table public.custom_categories (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
name character varying(50) not null,
measurement_type character varying(50) not null, -- e.g., 'numeric', 'text'
frequency text not null, -- e.g., 'Daily', 'Weekly', 'Monthly', 'All'
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint custom_categories_pkey primary key (id),
constraint custom_categories_user_id_fkey foreign KEY (user_id) references auth.users (id),
constraint custom_categories_frequency_check check (
(
frequency = any (array['All'::text, 'Daily'::text, 'Hourly'::text, 'Weekly'::text, 'Monthly'::text]) -- Added Weekly, Monthly based on common use cases
)
)
) TABLESPACE pg_default;
RLS: Users can only access their own categories Use Case: Custom metrics like "mood", "energy level", etc.
12. custom_measurements
Purpose: Values for custom measurement categories
-- custom_measurements table
create table public.custom_measurements (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
category_id uuid not null,
value numeric not null,
entry_date date not null,
entry_hour integer null, -- For hourly frequency
entry_timestamp timestamp with time zone not null default now(),
created_at timestamp with time zone not null default now(),
constraint custom_measurements_pkey primary key (id),
constraint custom_measurements_unique_entry unique (user_id, category_id, entry_date, entry_hour), -- Ensure uniqueness based on frequency
constraint custom_measurements_category_id_fkey foreign KEY (category_id) references custom_categories (id) on delete CASCADE,
constraint custom_measurements_user_id_fkey foreign KEY (user_id) references auth.users (id)
) TABLESPACE pg_default;
RLS: Users can only access their own measurements Special Features:
- Supports both daily and hourly tracking
- Links to user-defined categories
13. water_intake
Purpose: Daily water consumption tracking
-- water_intake table
create table public.water_intake (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
entry_date date not null,
glasses_consumed integer not null,
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint water_intake_pkey primary key (id),
constraint water_intake_user_date_unique unique (user_id, entry_date),
constraint water_intake_user_id_fkey foreign KEY (user_id) references auth.users (id)
) TABLESPACE pg_default;
RLS: Users can only access their own water intake Use Case: Simple daily hydration tracking
Exercise Tables
14. exercises
Purpose: Master exercise database
-- exercises table
create table public.exercises (
id uuid not null default gen_random_uuid (),
name character varying(255) not null,
category character varying(50) null, -- e.g., 'cardio', 'strength'
calories_per_hour integer null, -- Estimated calories burned per hour
description text null,
is_custom boolean null default false,
user_id uuid null, -- Creator user_id if is_custom is true
created_at timestamp with time zone null default now(),
updated_at timestamp with time zone null default now(),
shared_with_public boolean null default false,
constraint exercises_pkey primary key (id),
constraint exercises_user_id_fkey foreign KEY (user_id) references auth.users (id)
) TABLESPACE pg_default;
RLS: Users can access public exercises and their own custom exercises Special Features:
- Public exercises (user_id is NULL) accessible to all
- Custom exercises linked to specific users
15. exercise_entries
Purpose: Daily exercise log
-- exercise_entries table
create table public.exercise_entries (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
exercise_id uuid not null,
duration_minutes integer not null,
calories_burned integer not null,
entry_date date null,
notes text null,
created_at timestamp with time zone null default now(),
constraint exercise_entries_pkey primary key (id),
constraint exercise_entries_exercise_id_fkey foreign KEY (exercise_id) references exercises (id),
constraint exercise_entries_user_id_fkey foreign KEY (user_id) references auth.users (id)
) TABLESPACE pg_default;
RLS: Users can only access their own exercise entries
Family & Access Control Tables
16. family_access
Purpose: Manage family member access to user data
-- family_access table
create table public.family_access (
id uuid not null default gen_random_uuid (),
owner_user_id uuid not null, -- data owner
family_user_id uuid not null, -- accessing user
family_email text not null,
access_permissions jsonb not null default '{}'::jsonb,
access_start_date timestamp with time zone not null default now(),
access_end_date timestamp with time zone null,
is_active boolean not null default true,
status text not null default 'pending',
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint family_access_pkey primary key (id),
constraint family_access_owner_user_id_fkey foreign KEY (owner_user_id) references auth.users (id),
constraint family_access_family_user_id_fkey foreign KEY (family_user_id) references auth.users (id)
) TABLESPACE pg_default;
Permission Structure:
{
"calorie": boolean, // Food diary access
"checkin": boolean, // Body measurements access
"reports": boolean, // Analytics access
"food_list": boolean // Food database access
}
RLS: Users can manage access grants where they are the owner Special Features:
- Time-limited access support
- Granular permission control
- Status tracking (pending/active/expired)
AI & Chat Tables
17. ai_service_settings
Purpose: Store AI service configuration per user
-- ai_service_settings table
create table public.ai_service_settings (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
service_name character varying(255) not null, -- e.g., 'OpenAI', 'Google Gemini'
service_type character varying(50) not null, -- e.g., 'openai', 'google'
api_key text not null,
is_active boolean not null default true,
model_name character varying(255) null, -- Specific model used
system_prompt text null, -- Custom system prompt for the AI
custom_url text null, -- For custom or compatible services
created_at timestamp with time zone not null default now(),
updated_at timestamp with time zone not null default now(),
constraint ai_service_settings_pkey primary key (id),
constraint ai_service_settings_user_id_fkey foreign KEY (user_id) references auth.users (id)
) TABLESPACE pg_default;
RLS: Users can only access their own AI settings Security: API keys should be encrypted at application level
18. sparky_chat_history
Purpose: Store AI chat conversation history
-- sparky_chat_history table
create table public.sparky_chat_history (
id uuid not null default gen_random_uuid (),
user_id uuid not null,
session_id uuid not null default gen_random_uuid (), -- To group messages by session
message_type character varying(50) not null, -- 'user' or 'assistant'
content text not null, -- The message content
created_at timestamp with time zone not null default now(),
metadata jsonb null, -- To store additional data like food options, image URLs, etc.
-- Deprecated fields, kept for history but not actively used in new workflow:
message text null,
response text null,
image_url text null,
constraint sparky_chat_history_pkey primary key (id),
constraint sparky_chat_history_user_id_fkey foreign KEY (user_id) references auth.users (id)
) TABLESPACE pg_default;
RLS: Users can only access their own chat history Special Features:
- Session grouping for conversation context
- Metadata for structured AI responses
- Image support for food photo analysis
Database Functions
Access Control Functions
can_access_user_data(target_user_id, permission_type)
: Check if current user can access another user's datacheck_family_access(family_user_id, owner_user_id, permission)
: Verify family access permissionsget_accessible_users(user_id)
: Get list of users accessible to current userfind_user_by_email(email)
: Find user by email (security definer)
Goal Management Functions
get_goals_for_date(user_id, date)
: Get effective goals for specific datemanage_goal_timeline(user_id, start_date, calories, protein, carbs, fat, water_goal)
: Update goals with timeline management
User Management Functions
handle_new_user()
: Trigger function for new user setupcreate_user_preferences()
: Trigger function to create default preferences
RLS Security Model
Access Patterns
- Own Data Access: Users can always access their own data
- Family Access: Controlled by family_access table permissions
- Public Data: Foods and Exercises with user_id=NULL are publicly accessible
- No Cross-User Access: No direct access to other users' data without explicit permission
Permission Inheritance
reports
permission grants read access tocalorie
andcheckin
datafood_list
permission grants read access to food-related data- Write permissions are explicitly granted, no inheritance for writes
Security Principles
- All tables have RLS enabled (where applicable and feasible within the migration system)
- Default deny access model
- Explicit permission grants only
- Time-based access control support
- Audit trail preservation
Note on RLS for New Meal Tables:
Due to the dependency on the auth.uid()
function, which is not natively available during database migrations in this environment, Row Level Security for the meals
, meal_foods
, and meal_plans
tables is currently handled at the application layer. This ensures proper data isolation and access control through backend logic rather than direct database policies. Future enhancements may include implementing a custom auth.uid()
function or integrating with an authentication service that provides this functionality to enable full database-level RLS for these tables.