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…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.