docsDatabase Schema

Database Schema

Watermelon uses Supabase (PostgreSQL) for persistent data. All tables are in the public schema with Row-Level Security (RLS) policies.


Profiles

Stores user identity and gamification statistics.

ColumnTypeDescription
iduuidPrimary key, matches Supabase auth user ID
created_attimestamptzAccount creation time
emailtextUser email (nullable)
usernametextUnique username
display_nametextPublic display name
avatar_urltextProfile picture URL (DiceBear / Gravatar)
plantextFREE, PREMIUM_INDIVIDUAL, PREMIUM_FAMILY, etc.
is_bannedbooleanAdmin ban flag
telegram_idtextTelegram user ID for bot integration
fcm_tokentextFirebase Cloud Messaging token for push notifications

Gamification Columns

ColumnTypeDescription
xp_totalintTotal experience points
xp_levelintComputed level: GREATEST(1, FLOOR(SQRT(xp_total::FLOAT / 100.0)))
rank_tiertextComputed from hours listened (e.g. 🌱 Seed Listener β†’ πŸ‘‘ Eternal Echo)
hours_listenedfloatTotal listening hours
minutes_listenedfloatTotal listening minutes
streak_daysintCurrent consecutive listening streak
longest_streakintAll-time longest streak
songs_playedintTotal play events
songs_completedintSongs listened to >30s
artists_discoveredintUnique artists played
playlists_createdintUser-created playlists count
liked_songs_countintFavorited songs count
top_genretextMost-played genre
top_artisttextMost-played artist

Rank Tiers

Computed automatically via trigger on hours_listened update:

HoursTier
0🌱 Seed Listener
5πŸƒ Sprout Wave
15🎧 Pulse Rider
35🌊 Echo Drift
60🎢 Resonance
100πŸ“€ Vinyl Hunter
160🎡 Frequency Soul
250🌌 NovaBeat
350πŸ’Ώ Harmonic Flow
500πŸ”₯ Reverb X
700⚑ Soundrift
950🌠 Celestia Tone
1200🎼 Wave Architect
1600🌈 Spectrum Lord
2000πŸ‘‘ Eternal Echo

Playlists

ColumnTypeDescription
iduuidPrimary key
created_attimestamptzCreation time
user_iduuidOwner’s user ID
nametextPlaylist name
descriptiontextOptional description
cover_urltextCover image URL
tagstext[]Array of genre/label tags
like_countintCommunity likes
share_codetextUnique shareable code
is_publicbooleanVisibility flag
updated_attimestamptzLast modification time

Row-Level Security

  • Users can view their own playlists + any is_public = true playlist
  • Users can update/delete only their own playlists

Playlist Songs

Junction table for playlist ↔ song relationships with ordering.

ColumnTypeDescription
iduuidPrimary key
playlist_iduuid→ playlists.id
song_iduuid→ songs.id
added_attimestamptzWhen added
order_indexintPosition in playlist

Songs

Cached metadata from YouTube Music.

ColumnTypeDescription
iduuidPrimary key
created_attimestamptzCache time
video_idtextYouTube video ID
titletextSong title
artisttextArtist name
albumtextAlbum name
thumbnail_urltextYouTube thumbnail URL
duration_secondsintLength in seconds

Favorites

ColumnTypeDescription
iduuidPrimary key
user_iduuid→ profiles.id
song_iduuid→ songs.id
created_attimestamptzWhen favorited

Play Sessions

Listening history for analytics and recommendations.

ColumnTypeDescription
iduuidPrimary key
created_attimestamptzPlay start time
user_iduuid→ profiles.id
song_iduuid→ songs.id
play_duration_secondsintHow long user listened
was_skippedbooleanTrue if skipped before 30s
completedbooleanTrue if listened >30s

User Actions

Analytics for recommendation engine.

ColumnTypeDescription
iduuidPrimary key
created_attimestamptzAction time
user_iduuid→ profiles.id
song_iduuid→ songs.id
action_typetextPLAY, SKIP, COMPLETE, LIKE, UNLIKE
contexttextWhere the action happened (SEARCH, PLAYLIST, RADIO)

Achievements

Badge definitions.

ColumnTypeDescription
iduuidPrimary key
created_attimestamptzCreation time
codetextUnique slug (e.g. night_owl)
nametextDisplay name (e.g. β€œNight Owl”)
descriptiontextWhat you did to earn it
emojitextπŸ¦‰ etc.
xp_valueintXP awarded
requirement_typetextSTREAK, PLAY_COUNT, HOURS, DISCOVERY, PLAYLIST_COUNT
requirement_valueintThreshold to unlock

Built-in Badges

CodeNameEmojiRequirement
first_listenFirst Listen🎡1st play
night_owlNight OwlπŸ¦‰Play at 2AM
explorerExplorer🧭Discover 10 new artists
playlist_proPlaylist ProπŸ“‹Create 5 playlists
early_birdEarly Bird🐦Play before 7AM
marathonerMarathonerπŸƒ50+ hours listened

User Achievements

Junction table: which user unlocked which badge.

ColumnTypeDescription
iduuidPrimary key
user_iduuid→ profiles.id
achievement_iduuid→ achievements.id
unlocked_attimestamptzWhen earned
seenbooleanWhether user has viewed it

Premium Requests

Payment verification queue for admin approval.

ColumnTypeDescription
iduuidPrimary key
created_attimestamptzRequest time
user_iduuid→ profiles.id (nullable)
emailtextUser email
plantextWhich plan purchased
order_idtextRazorpay order ID
payment_idtextRazorpay payment ID
amountintAmount in paise
currencytextINR
statustextpending, approved, rejected

Indexes

-- Performance indexes for leaderboard queries
CREATE INDEX idx_profiles_hours_listened ON public.profiles(hours_listened DESC);
CREATE INDEX idx_profiles_rank_tier ON public.profiles(rank_tier);
CREATE INDEX idx_playlist_songs_playlist_id ON public.playlist_songs(playlist_id);
CREATE INDEX idx_favorites_user_id ON public.favorites(user_id);
CREATE INDEX idx_play_sessions_user_id ON public.play_sessions(user_id);
CREATE INDEX idx_user_actions_user_id ON public.user_actions(user_id);

Triggers

Auto-Rank Assignment

CREATE TRIGGER on_profile_rank_update
  BEFORE UPDATE ON public.profiles
  FOR EACH ROW WHEN (NEW.hours_listened IS DISTINCT FROM OLD.hours_listened)
  EXECUTE FUNCTION auto_assign_rank_tier();

Auto-Level Up

CREATE TRIGGER on_profile_level_up
  BEFORE UPDATE ON public.profiles
  FOR EACH ROW
  EXECUTE FUNCTION auto_update_xp_level();

For more details, see the Self-Hosting Guide where the full SQL setup script is documented.