friendships
Overview
The friendships table manages bidirectional friend relationships. CommonPlace only supports mutual friendships -- there are no one-way "follows." A friendship begins as a request (status = 'pending') and becomes active when accepted. The requester/addressee distinction tracks who initiated.
Relevant Invariants
- Invariant #1: "Participation Is Always Voluntary" -- Friend requests can be declined or ignored without consequence
- Invariant #7: "Human-Scale Social Contexts" -- Mutual friendships enforce small, intentional networks
Schema
-- Pre-existing table (inferred from frontend code)
CREATE TABLE friendships (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
requester_id UUID REFERENCES auth.users(id) NOT NULL,
addressee_id UUID REFERENCES auth.users(id) NOT NULL,
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'declined')),
responded_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
stepped_back_by UUID REFERENCES auth.users(id),
stepped_back_at TIMESTAMPTZ,
stepped_back_until TIMESTAMPTZ,
stepped_back_reason TEXT,
UNIQUE(requester_id, addressee_id)
);
Columns
| Column | Type | Nullable | Default | Description |
|---|---|---|---|---|
id | uuid | No | uuid_generate_v4() | Primary key |
requester_id | uuid | No | -- | User who sent the friend request |
addressee_id | uuid | No | -- | User who received the request |
status | text | No | 'pending' | Request state: pending, accepted, declined |
responded_at | timestamptz | Yes | -- | When request was accepted/declined |
created_at | timestamptz | No | NOW() | When request was sent |
stepped_back_by | uuid | Yes | -- | Which user initiated the step-back (null = not stepped back) |
stepped_back_at | timestamptz | Yes | -- | When the step-back was initiated |
stepped_back_until | timestamptz | Yes | -- | Optional auto-restore date (null = indefinite) |
stepped_back_reason | text | Yes | -- | Private note visible only to the stepper |
RLS Policies
-- SELECT: Users can see their own friendships
CREATE POLICY "Users can view own friendships"
ON friendships FOR SELECT
USING (auth.uid() = requester_id OR auth.uid() = addressee_id);
-- INSERT: Users can send friend requests
CREATE POLICY "Users can send friend requests"
ON friendships FOR INSERT
WITH CHECK (auth.uid() = requester_id);
-- UPDATE: Addressee can accept/decline requests
CREATE POLICY "Users can respond to friend requests"
ON friendships FOR UPDATE
USING (auth.uid() = addressee_id);
-- DELETE: Either party can remove friendship
CREATE POLICY "Users can remove friendships"
ON friendships FOR DELETE
USING (auth.uid() = requester_id OR auth.uid() = addressee_id);
Step-Back Feature
The stepped_back_* columns enable a private, temporary break from seeing a friend's posts without blocking them. Key properties:
- Private: The other party is never notified
- Reversible: Clearing the columns restores normal visibility
- Optional expiration:
stepped_back_untilallows auto-restore after a set time - Feed filtering: The posts SELECT RLS policy excludes posts from stepped-back friends
The frontend hook useSteppedBack (in src/hooks/useSteppedBack.js) manages this state and auto-checks for expired step-backs every hour.
Related
- profiles -- Both parties in the friendship
- blocks -- Blocking removes friendships
- circles -- Friends can be organized into circles
- relationship_edges -- Custom relationship labels between friends
Last updated: 2026-02-08