Skip to main content

What It Does

Standard social follow system. Following a user adds their content to your Home (Following) feed.

User Flow

User taps FollowButton on any profile card, detail header, or user list → local optimistic UI toggle (component-level state) → API call → cache invalidation on success. On error, the component reverts to previous state and shows a toast.

Screens

ScreenPurpose
app/followers/[username].tsxList of user’s followers
app/following/[username].tsxList of users being followed
app/connections/[username].tsxCombined connections view

Components

FollowButton (src/components/common/FollowButton.tsx), UserCard, ProfileCard

Data Access Pattern

The mobile app calls Supabase directly — supabase.from('follows').insert/delete and supabase.rpc('get_user_followers'). The web API routes exist for the web client (Phase 2).

Hooks & Mutation Patterns

useUser (src/hooks/api/useUser.ts) exposes three follow mutation hooks:
HookBehavior
useFollowUser()Insert-only: calls supabase.from('follows').insert, handles duplicate gracefully (code 23505). No React Query optimistic update — invalidates caches on success. The FollowButton component manages its own local optimistic state.
useUnfollowUser()Delete-only: calls supabase.from('follows').delete. Same pattern — no hook-level optimistic update, component handles local state.
useToggleFollow()Convenience wrapper: calls follow or unfollow based on isFollowing param. Same invalidation pattern.
All three invalidate queryKeys.users.profile, queryKeys.users.followers, queryKeys.users.following, and all ['profile', 'data'] queries on success (via shared invalidateFollowQueries helper). useFollowers(userId) / useFollowing(userId) — Infinite query with FOLLOWERS_PAGE_SIZE = 30, calls get_user_followers / get_user_following RPC.

Web API Routes (Phase 2)

MethodRoutePurpose
POST/api/users/[id]/followFollow a user
DELETE/api/users/[id]/followUnfollow a user
GET/api/users/[id]/followersGet follower list
GET/api/users/[id]/followingGet following list

RPC Functions

FunctionParametersReturns
get_user_followersp_user_id, p_current_user_id, p_limit (50), p_offset (0)JSON — paginated follower list with follow-back status
get_user_followingp_user_id, p_current_user_id, p_limit (50), p_offset (0)JSON — paginated following list

Database

TableKey ColumnsConstraints
followsfollower_id (FK→profiles), following_id (FK→profiles), created_atComposite PK. CHECK: follower_id <> following_id (prevents self-follow)

RLS Policies

  • SELECT: true (public)
  • INSERT: auth.uid() = follower_id
  • DELETE: auth.uid() = follower_id

Triggers

TriggerTableEventsFunction
trigger_update_follow_countsfollowsINSERT, DELETEupdate_follow_counts() — updates both profiles.follower_count and profiles.following_count