Skip to content
Back to skills

02 Supabase Social Graph Engineer

ASecurity

Use when designing Postgres/Supabase schema, triggers, helper functions, and query patterns for follows, follower requests, mutuals, blocks, counters, and profile access relationships.

  • 18 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aishellfrontendsecurity

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add conectlens/lenserfight --skill 02-supabase-social-graph-engineer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 02 Supabase Social Graph Engineer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 02 Supabase Social Graph Engineer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/conectlens-02-supabase-social-graph-engineer/badge)](https://www.skillsdirectory.com/skills/conectlens-02-supabase-social-graph-engineer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: supabase-social-graph-engineer
description: Use when designing Postgres/Supabase schema, triggers, helper functions, and query patterns for follows, follower requests, mutuals, blocks, counters, and profile access relationships.
---

# Supabase Social Graph Engineer

## Mission
Design the canonical social graph for follows, approvals, blocks, and derived friendship.

## Required stance
Use an asymmetric follow model as the primitive. Do not use a symmetric friendship table as the foundation.

## Core tables

### `lensers.profiles`
Must include or reference:
- `profile_id`
- `account_id`
- `username`
- `slug`
- `visibility` enum: `public|private`
- `account_status` enum: `active|deactivated|pending_deletion|deleted`
- `deleted_at`
- `deletion_scheduled_for`
- `deactivated_at`
- summary fields for restricted shell

### `lensers.relationships`
One row per directed relationship from viewer to subject.

Columns:
- `source_profile_id`
- `target_profile_id`
- `status` enum: `pending|accepted|rejected|blocked|removed`
- `requested_at`
- `responded_at`
- `accepted_at`
- `removed_at`
- `is_close_circle` boolean default false
- `created_by_policy` text nullable
- unique `(source_profile_id, target_profile_id)`

### `lensers.profile_counters`
Denormalized counters:
- `followers_count`
- `following_count`
- `mutuals_count` optional
- `threads_count_public`
- `prompts_count_public`
- `badges_count`

### Optional `lensers.blocks`
You may keep blocks in `relationships.status='blocked'`, but a dedicated block table is cleaner if moderation rules grow.

## Required helper functions

### `fn_relationship_state(viewer_profile_id, subject_profile_id)`
Returns:
- direct relationship status
- reverse relationship status
- `is_mutual_follow`
- `is_blocked_any_direction`

### `fn_can_view_profile(viewer_auth_uid, subject_profile_id)`
Returns:
- access outcome
- access reason
- can_view_full_profile
- can_view_restricted_shell
- can_request_follow
- can_cancel_request
- can_unfollow

### `fn_request_follow(subject_profile_id)`
Rules:
- public account => create accepted follow immediately
- private active account => create pending request
- deactivated / pending_deletion / deleted => reject
- blocked relation any direction => reject

### `fn_accept_follow_request(source_profile_id)`
Only target owner may accept.

### `fn_remove_follow(target_profile_id)`
Soft-remove relation or switch to `removed`.

## Derived friendship

Friendship is computed, not stored as the main truth:
- `is_friend = exists accepted(A->B) and accepted(B->A)`

Only materialize it if analytics/search need acceleration.

## Counter maintenance

Use triggers or queued jobs to maintain:
- follower / following counts
- public-content counts

Favor correctness over micro-optimization.

## Query rules

1. Never join raw relations in ad hoc frontend queries for access decisions.
2. Expose a single profile-access RPC or security-definer view.
3. All search/discovery endpoints must filter `account_status='active'`.

## Suggested indexes

- unique `(source_profile_id, target_profile_id)`
- btree `(target_profile_id, status)`
- btree `(source_profile_id, status)`
- partial index for `status='pending'`
- partial index for `status='accepted'`
- index on `profiles(username)`
- index on `profiles(slug)`
- index on `profiles(account_status, visibility)`

## Deliverables

Produce:
- normalized schema
- constraints
- indexes
- trigger plan
- RPC contract

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…