postgres-supabase-expert
Advanced guide for PostgreSQL and Supabase. Covers schema design, RLS, query optimization, migrations, Edge Functions, and database security.
SKILL.mdskills/postgres-supabase-expert/SKILL.md
GitHubPostgres & Supabase Expert Guide
This skill provides comprehensive rules for designing, optimizing, and securing databases on PostgreSQL and the Supabase platform.
🏗 Schema Design
- Normalization: Follow 3NF unless denormalization is required for performance.
- Data Types: Use appropriate types (e.g.,
UUIDfor IDs,TIMESTAMPTZfor dates,JSONBfor flexible data). - Constraints: Use
NOT NULL,UNIQUE, andCHECKconstraints to ensure data integrity at the database level. - Indexes: Use
B-treefor equality and range queries,GINfor JSONB/Arrays, andBRINfor large sequential datasets.
🛡 Security & Privacy
R-01 RLS DEFAULT & POLICY ENFORCEMENT (Moved from engineering-contracts former §17 — Canonical Authority for Postgres/Supabase)
SCOPE: Applies ONLY to PostgreSQL and Supabase. Do NOT use RLS syntax on MySQL, SQLite, MongoDB, Cassandra, or non-Postgres databases — for those see engineering-contracts §17 pointer.
Enforcement Rule (HARD — every new table):
- For EVERY new table: immediately add
ALTER TABLE <schema>.<table> ENABLE ROW LEVEL SECURITY;in the migration. - Define explicit read/write policies per role (e.g.
organizer_select_policy,admin_all_policy). A table with RLS enabled but ZERO policies = no rows can be read/written (default deny) — good. - Add an Acceptance Criteria in the SPEC (if using che-spec standalone or /che-act SM §0.5 SPEC gate) specifically for RLS: e.g.
- [MUST] AC-RLS GIVEN Organizer A authenticated WHEN querying tickets/events owned by Organizer B THEN HTTP 403 or 404 returned | TEST=qa_integrationliteral format in §4. This is validated during QA. - ONLY exception (allowed logged + user approved DOUBLE CONFIRMATION):
- Pure lookup tables (enum reference tables, immutable public seed data for everyone) → RLS not needed, BUT:
- Explicitly mark in Non-Goals / Data Model notes of the SPEC.
- Log exception + user approval in
decisions.log.jsonl - Table name + reason documented in migration notes.
- Pure lookup tables (enum reference tables, immutable public seed data for everyone) → RLS not needed, BUT:
Additional Security & Privacy (R-02 …)
- RLS per-table minimum 1 policy of each kind where applicable: SELECT/INSERT/UPDATE/DELETE — don't leave a write-path open if only SELECT was thought through.
- Encryption: Use
pg_sodiumfor sensitive data encryption at-rest where PII cannot be hashed. - Schemas: Isolate data using different Postgres schemas (e.g.
public,private,auth) + grant usage per role. - PII: Avoid storing raw PII where possible. Use hashing (with NOTIFICATION_PII_HASH_SECRET-style secret) for correlation IDs, or pg_crypto/pg_sodium encryption if storage is unavoidable.
auth.uid()default-deny pattern: In Supabase, start every policy withauth.uid() = <owner_column>— then add admin/org overrides on top. Never write a policy that istrueforanonrole unless it is genuinely a public-visible lookup table (and §R-01 exception 4 was filed).
🚀 Performance & Optimization
- EXPLAIN ANALYZE: Always use to identify slow queries and sequential scans.
- Vacuum & Analyze: Monitor bloat and keep statistics up to date.
- Connection Pooling: Use
SupavisororPgBouncerfor high-concurrency applications. - Pagination: Prefer keyset pagination (cursor-based) over
OFFSETfor large datasets.
⚡ Supabase Features
- Edge Functions: Use for logic that requires external API calls or heavy processing. Use Deno/TypeScript.
- Realtime: Use sparingly for live updates. Filter channels to reduce overhead.
- Storage: Use for large assets. Enforce RLS on buckets and folders.
- Migrations: Use the Supabase CLI for declarative migrations. Never make manual changes in production.