supabase-pack
Complete Supabase integration skill pack with 30 skills covering authentication, database, storage, realtime, edge functions, and production operations. Flagship+ tier vendor pack.
Installation
Open Claude Code and run this command:
/plugin install supabase-pack@claude-code-plugins-plus
Use --global to install for all projects, or --project for current project only.
What It Does
> 30 production-grade Claude Code skills for Supabase: real @supabase/supabase-js v2 client methods, real PostgreSQL RLS policies, real Edge Functions, real Supabase CLI workflows.
Skills (30)
Deep Supabase diagnostics: pg_stat_statements for slow queries, lock debugging with pg_locks, connection leak detection, RLS policy conflicts, Edge Function cold starts, and Realtime connection drop analysis.
Supabase Advanced Troubleshooting
Overview
When basic debugging does not reveal the root cause, you need deep PostgreSQL diagnostics: pgstatstatements to find the slowest queries by cumulative execution time, pglocks to detect lock contention and deadlocks, pgstat_activity to find connection leaks, RLS policy conflict analysis to diagnose silent data filtering, Edge Function cold start profiling, and Realtime channel drop investigation. This skill covers every advanced diagnostic technique with real SQL queries and createClient from @supabase/supabase-js.
When to use: Slow query investigation, lock contention causing timeouts, connection pool exhaustion from leaks, RLS policies that silently filter or conflict, Edge Functions with unpredictable latency, or Realtime subscriptions that disconnect intermittently.
Prerequisites
- Supabase project with
pgstatstatementsextension enabled - Direct database access via SQL Editor or
psql @supabase/supabase-jsv2+ installed in your project- Supabase CLI for Edge Function logs
- Familiarity with PostgreSQL system catalogs
Authentication
Every technique here runs against your own project's Postgres, so authenticate with
project-scoped credentials, never a shared key:
psql/ SQL Editor — connect with the project's direct connection string or
pooled Supavisor URI from Dashboard → Settings → Database. Keep the database
password in an env var (PGPASSWORD / .pgpass), never inline in a command.
- SDK (
createClient) — pass the service-role key (SUPABASESERVICEROLE_KEY)
for the diagnostic RPCs below, since they read pgstatactivity and system
catalogs that the anon key cannot. Load it from the environment; never commit it.
supabaseCLI — runsupabase loginonce (stores a personal access token),
then supabase link --project-ref for Edge Function logs.
Instructions
Work the three diagnostic tracks in the order the symptom points to. Each track's
full query set and SDK helper lives in a reference file — SKILL.md carries the
skeleton so you can start immediately, then drill in for the complete toolkit.
Step 1: pgstatstatements and slow query analysis
Enable pgstatstatements, then rank queries by total execution time, call
frequency, and cache-hit ratio to find the real cost centers. Follow up with
EXPLAIN (ANALYZE, BUFFERS) on the worst offenders and add targeted indexes
Use when choosing how to integrate Supabase into a specific stack — setting up Next.
Supabase Architecture Variants
Overview
Every Supabase createClient configuration turns on two questions: where the client runs (browser vs server) and which key it uses (anon respects RLS; service_role bypasses it). This skill supplies production-ready patterns for five architectures — Next.js SSR, SPA, Mobile, Serverless Edge Functions, and Multi-tenant isolation.
Prerequisites
@supabase/supabase-jsv2+ installed@supabase/ssrpackage for Next.js SSR (v0.5+)- Supabase project with URL,
anonkey, andservice_rolekey - TypeScript project with generated database types (
supabase gen types typescript) - For mobile: React Native with Expo or bare workflow
Instructions
Pick the architecture that matches the target stack, then follow the linked walkthrough for the full, copy-ready client setup.
| Architecture | Client(s) | Key | Session storage |
|---|---|---|---|
| Next.js SSR | Server (cookies) + browser + admin | anon in-request, service_role server-only |
HTTP cookies |
| SPA (React/Vue) | Single browser client | anon only |
localStorage |
| Mobile (React Native) | Single native client | anon only |
AsyncStorage |
| Serverless (Edge Functions) | Per-request client | anon (forwarded JWT) or service_role |
none (stateless) |
| Multi-tenant | Any of the above | anon + RLS, or schema-per-tenant |
per host pattern |
Step 1 — Next.js SSR (App Router)
Next.js App Router needs two separate clients: a server client that reads/writes auth cookies via @supabase/ssr, and a browser client for client components. A third service_role admin client is used only in Server Actions/Route Handlers and must never reach the browser. The browser client is the minimal skeleton:
// lib/supabase/client.ts
'use client'
import { createBrowserClient } from '@supabase/ssr'
import type { Database } from '../database.types'
export function createSupabaseBrowser() {
return createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! // anon key only — respects RLS
)
}
Full walkthrough — server client, admin client, middleware session refresh, server component usage, Server Actions, and the OAuth callback route: Next.js SSR patterns<
'Implement Supabase Auth (signUp, signIn, OAuth, session management),.
Supabase Auth + Storage + Realtime Core
Overview
Implement the three pillars that turn a Supabase database into a full application backend: user authentication (email/password, OAuth, magic links, session lifecycle), file storage (uploads, downloads, signed URLs, bucket-level RLS policies), and real-time subscriptions (Postgres change events, client-to-client broadcast, presence tracking). Every operation integrates with Row-Level Security through auth.uid().
Each pillar below carries a lean skeleton in this file; the full, copy-paste walkthroughs live in references/ so this file stays scannable.
Prerequisites
- Supabase project created at supabase.com/dashboard
@supabase/supabase-jsv2 installed (npm install @supabase/supabase-js)SUPABASEURLandSUPABASEANON_KEYavailable from project Settings > API- For Python:
pip install supabase(wrapspostgrest-py,gotrue-py,storage3,realtime-py)
Instructions
Read the file (Read), edit or create the client and route/component code (Write, Edit), and grep the project (Grep) to reuse an existing Supabase client before creating a new one. Use Bash(npm:) to install the SDK and Bash(supabase:) to run migrations/policies.
Step 1: Auth — registration, login, OAuth
Initialize the client once, then wire the flows your app needs. The skeleton:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!)
// Email/password
await supabase.auth.signUp({ email, password })
await supabase.auth.signInWithPassword({ email, password })
// OAuth — redirect the user to data.url
const { data } = await supabase.auth.signInWithOAuth({ provider: 'google' })
// React to session changes (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED)
supabase.auth.onAuthStateChange((event, session) => { /* update UI */ })
Full auth walkthrough — OAuth callback, magic link, session lifecycle, password reset: references/auth.md. Python: references/python-examples.md.
Step 2: Storage — upload, download, secure with bucket policies
Public buckets serve via CDN URLs; private buckets require signed URLs. The skeleton:
// Upload to the signed-in user's own folder (RLS enforces ownership)
await supabase.storage.from('avatars').upload(`${userId}/avatar.png`, file, { upsert: true })
// Public URL (public bucket) vs. time-limited signed URL (private bucket)
supabase.storage.from('avatars').getPublicUrl(`${userId}/avatConfigure Supabase continuous-integration and deployment pipelines with GitHub Actions: link projects, push migrations, deploy Edge Functions, generate types, and run tests against local Supabase instances.
Supabase CI Integration
Overview
Build GitHub Actions workflows that automate the full Supabase lifecycle: link projects in CI, push migrations on merge, deploy Edge Functions, generate TypeScript types, run tests against a local Supabase instance, and create preview branches for pull requests. Every database change gets validated before it reaches production.
The pull-request CI pipeline runs these stages, all detailed in ci-workflows.md:
- Start a local Supabase instance (unused services disabled for speed).
- Apply every migration from scratch with
npx supabase db reset. - Regenerate TypeScript types and fail the build on drift from the committed version.
- Run pgTAP database tests and the application test suite against the local instance.
- Stop Supabase (always, even on failure).
Prerequisites
- GitHub repository with Actions enabled
- Supabase project created at supabase.com/dashboard
- Supabase CLI initialized locally (
npx supabase init) - Node.js 18+ in your project
@supabase/supabase-jsinstalled:
npm install @supabase/supabase-js
Instructions
Step 1: Configure GitHub Secrets and Link in CI
Store credentials as GitHub repository secrets. The CI pipeline uses these to authenticate with your Supabase project without exposing tokens in code.
# Set secrets via GitHub CLI
gh secret set SUPABASE_ACCESS_TOKEN --body "<your-access-token>"
gh secret set SUPABASE_DB_PASSWORD --body "<your-database-password>"
gh secret set SUPABASE_PROJECT_REF --body "<your-project-ref>"
Generate your access token at supabase.com/dashboard/account/tokens. Find your project ref in Project Settings > General.
Link the project in any CI job that needs remote access:
- name: Install Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
- name: Link Supabase project
run: npx supabase link --project-ref ${{ secrets.SUPABASE_PROJECT_REF }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
Step 2: CI Workflow — Test, Validate Migrations, and Generate Types
Add 'Diagnose and fix Supabase errors across PostgREST, PostgreSQL, Auth,. Diagnostic guide for Supabase errors across PostgREST ( The workflow is three steps: capture the error object, classify it by layer and code, then apply and verify the fix. Full step-by-step code lives in the diagnostic walkthrough; complete lookup tables are in the error reference. Every Supabase SDK call returns a If Match the code prefix to its subsystem, then look it up in the Error Handling tables below: A missing code usually means the HTTP status is the signal: Apply the fix from the matching Error.github/workflows/supabase-ci.yml that runs on every pull request. It starts a local Supabase instance, runs db reset to apply all migrations from scratch, regenerates types and fails on drift (git diff --exit-code), then runs pgTAP and application tests. The default local dev keys are safe to commit — they only work against the local instance. Copy the complete workflow from
Supabase Common Errors
Overview
PGRST*), PostgreSQL (numeric codes), Auth, Storage, and Realtime. Identify the error layer, trace the root cause, and apply the correct fix — every SDK call returns { data, error } where data is null when error exists.Prerequisites
@supabase/supabase-js installed (npm install @supabase/supabase-js)SUPABASEURL and SUPABASEANONKEY (or SUPABASESERVICEROLEKEY) configurednpx supabase --version)Instructions
Step 1 — Capture the Error Object
{ data, error } tuple. Never assume data exists — always destructure and check error first, because data is null whenever error is set.
const { data, error } = await supabase.from('todos').select('*')
if (error) {
console.error(`[${error.code}] ${error.message}`)
return // data is null here — do not touch it
}
console.log(`Found ${data.length} rows`)
error is undefined rather than null, upgrade to @supabase/supabase-js@2.x. See the walkthrough for the full guard pattern.Step 2 — Identify the Error Layer and Code
PGRST** → PostgREST (API gateway: JWT, query parsing, schema)42501, 23505) → PostgreSQL engine (RLS, constraints, migrations)AuthApiError → Auth service (credentials, confirmation, token expiry)StorageApiError → Storage service (bucket, RLS on storage.objects, size limits)401 → bad/missing SUPABASEANONKEY; 500 → an unhandled exception in a database function. A paste-ready diagnoseSupabaseError() classifier is in the walkthrough.Step 3 — Apply the Fix and Verify
'Optimize Supabase costs through plan selection, database tuning, storage.
Supabase Cost Tuning
Overview
Reduce Supabase spend by auditing usage against plan limits, eliminating database and storage waste, and right-sizing compute resources. The three biggest levers: database optimization (vacuum, index cleanup, archival), storage lifecycle management (compress before upload, orphan cleanup), and connection pooling to reduce compute add-on requirements.
Work the three steps below in order — audit first to find where the money goes, then optimize the biggest offenders, then right-size compute. Each step keeps a representative snippet inline; the full query and script sets live in references/ so this file stays scannable.
Prerequisites
- Supabase project with Dashboard access (Settings > Billing)
@supabase/supabase-jsinstalled:npm install @supabase/supabase-js- Service role key for admin operations (storage audit, cleanup scripts)
- SQL editor access (Dashboard > SQL Editor or
psqlconnection)
Pricing Reference
| Resource | Free Tier | Pro ($25/mo) | Team ($599/mo) |
|---|---|---|---|
| Database | 500 MB | 8 GB included, $0.125/GB extra | 8 GB included |
| Storage | 1 GB | 100 GB included, $0.021/GB extra | 100 GB included |
| Bandwidth | 5 GB | 250 GB included, $0.09/GB extra | 250 GB included |
| Edge Functions | 500K invocations | 2M invocations, $2/million extra | 2M invocations |
| Realtime | 200 concurrent | 500 concurrent | 500 concurrent |
| Auth MAU | 50,000 | 100,000 | 100,000 |
Compute add-ons (Pro and above):
| Instance | vCPUs | RAM | Price |
|---|---|---|---|
| Micro | 2 | 1 GB | Included with Pro |
| Small | 2 | 2 GB | $25/mo |
| Medium | 2 | 4 GB | $50/mo |
| Large | 4 | 8 GB | $100/mo |
| XL | 8 | 16 GB | $200/mo |
| 2XL | 16 | 32 GB | $400/mo |
Decision framework: Read replicas ($25/mo each) beat scaling up when reads dominate and geographic distribution is needed. Connection pooling (Supavisor, free) reduces compute pressure from idle connections.
Instructions
Step 1: Audit current usage and identify cost drivers
Find where the database budget is going before changing anything. Run the audit queries in the SQL Editor to surface the biggest tables, unused indexes, dead-tuple bloat, and connection count. Start with total size:
Implement GDPR/CCPA compliance with Supabase: RLS for data isolation, user deletion via auth.
Supabase Data Handling
Overview
GDPR and CCPA compliance with Supabase uses a layered approach: Row Level Security (RLS) for tenant data isolation, supabase.auth.admin.deleteUser() for right-to-deletion, SQL-based exports for subject access requests, PII detection across columns, automated retention with pg_cron, and point-in-time recovery for backup/restore. Every requirement maps to a real Supabase SDK method or PostgreSQL feature.
When to use: Implement GDPR right-to-deletion, respond to data subject access requests (DSARs), audit PII in the database, configure automated retention, set up tenant isolation with RLS, or plan backup/restore procedures.
Prerequisites
@supabase/supabase-jsv2+ with service role key for admin operations- Supabase project on Pro plan (for
pg_cronand point-in-time recovery) - Understanding of GDPR Articles 15-17 (access, rectification, erasure)
- Database access via SQL Editor or
psqlfor schema changes
Instructions
Step 1: RLS for Data Isolation and PII Column Management
Enable RLS on every table holding user data, then classify PII columns so later deletion and export steps know what to touch. The RLS skeleton is one USING (auth.uid() = ...) policy per access pattern:
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "users_read_own_profile" ON public.profiles
FOR SELECT USING (auth.uid() = id);
Pair the policies with a PII audit — an informationschema.columns scan by naming pattern, COMMENT ON COLUMN tags, a piiregistry view, and an SDK-side regex scanner for emails, phones, SSNs, and IPs.
See RLS and PII reference for the full multi-tenant policy set, the PII audit SQL, the pii_registry view, and the scanTableForPII() SDK scanner.
Step 2: User Deletion and Data Export
Implement GDPR Article 17 (erasure) and Article 15 (access). Deletion runs in cascade order — application tables, then storage files, then the auth user, then an immutable audit-log entry that must survive the erasure:
// Cascade order matters: children before parents, auth user last
const tablesToPurge = ['comments', 'orders', 'documents', 'profiles'];
// ...delete rows, remove storage files, then:
await supabase.auth.admin.deleteUser(userId);
await supabase.from('gdpr_audit_log').insert({ action: 'USER_DELETION', subject_id: userId });
Export is the inverse: read every user-scoped table plus storage listings into one JSON payload and log a DATA_EXPORT audit row.
See deletio
'Collect Supabase diagnostic info for troubleshooting and support tickets.
Supabase Debug Bundle
Overview
Collect a comprehensive, redacted diagnostic bundle from a Supabase project. Tests connectivity, auth, Realtime, Storage, RLS policy behavior, and database health — then packages everything into a single archive safe for sharing with Supabase support.
Current State
!node --version 2>/dev/null || echo 'Node.js not found'
!npx supabase --version 2>/dev/null || echo 'Supabase CLI not found'
!npm list @supabase/supabase-js 2>/dev/null | grep supabase || echo '@supabase/supabase-js not installed'
Prerequisites
- Node.js 18+ with
@supabase/supabase-jsv2 installed in the project - Supabase CLI installed (
npm i -g supabaseornpx supabase) - Environment variables set:
SUPABASEURLandSUPABASEANONKEY(minimum);SUPABASESERVICEROLEKEYfor full diagnostics - Project linked via
supabase link --project-ref(for CLI commands)
Instructions
Step 1: Gather Environment and Connectivity
Collect SDK version, project URL, key type, and test basic connectivity against the REST and Auth endpoints.
import { createClient } from '@supabase/supabase-js'
const url = process.env.SUPABASE_URL!
const anonKey = process.env.SUPABASE_ANON_KEY!
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY
// Identify which key is in use
const keyType = serviceKey ? 'service_role' : 'anon'
const supabase = createClient(url, serviceKey ?? anonKey, {
auth: { autoRefreshToken: false, persistSession: false }
})
const diagnostics: Record<string, unknown> = {}
// 1a — SDK + environment
diagnostics.environment = {
supabase_js_version: require('@supabase/supabase-js/package.json').version,
node_version: process.version,
project_url: url.replace(/https:\/\/([^.]+)\..*/, 'https://$1.***'),
key_type: keyType,
timestamp: new Date().toISOString(),
}
// 1b — REST API connectivity
const restStart = Date.now()
const restRes = await fetch(`${url}/rest/v1/`, {
headers: { apikey: anonKey, Authorization: `Bearer ${anonKey}` },
})
diagnostics.rest_api = {
status: restRes.status,
latency_ms: Date.now() - restStart,
ok: restRes.ok,
}
// 1c — Auth health
const authStart = Date.now()
const { data: sessionData, error: sessionErr } = await supabase.auth.getSession()
diagnostics.auth = {
status: sessionErr ? `error: ${sessionErr.message}` : 'ok',
has_session: !!sessionData?.session,
latency_ms: Date.now() - authStart,
}
// 1d — Database connectivity probe
const dbStart = Date.now()
const { error: dbErr } = await supabase.from('_test_ping').select('*').limit(1)
diagnostics.database = {
// 42P01 = table doesn'Deploy and manage Supabase projects in production.
Supabase Deploy Integration
Overview
Deploy and manage Supabase projects in production with confidence. This skill covers the full deployment lifecycle: pushing database migrations, deploying Edge Functions, managing secrets, executing zero-downtime rollouts with blue/green database branching, rolling back failed migrations, and verifying deployment health. All commands use the Supabase CLI with --project-ref for explicit project targeting.
SDK: @supabase/supabase-js — supabase.com/docs
Prerequisites
- Supabase CLI installed (
npm install -g supabaseornpx supabase) - Supabase project linked (
npx supabase link --project-ref) - Database migrations in
supabase/migrations/directory - Edge Functions in
supabase/functions/directory (if deploying functions) SUPABASEACCESSTOKENset for CI/non-interactive environments
Instructions
Step 1 — Push Database Migrations and Deploy Edge Functions
Apply pending database migrations to your production project, then deploy Edge Functions with their required secrets.
Database migrations:
# Apply all pending migrations to production
npx supabase db push --project-ref $PROJECT_REF
# Preview what will run without applying (dry run)
npx supabase db push --project-ref $PROJECT_REF --dry-run
# Check current migration status
npx supabase migration list --project-ref $PROJECT_REF
Each migration file in supabase/migrations/ is applied in timestamp order. The CLI tracks which migrations have already been applied and only runs new ones.
Edge Functions deployment:
# Deploy a single Edge Function
npx supabase functions deploy process-webhook --project-ref $PROJECT_REF
# Deploy all Edge Functions at once
npx supabase functions deploy --project-ref $PROJECT_REF
Secrets management — set environment variables for Edge Functions:
# Set individual secrets
npx supabase secrets set STRIPE_KEY=sk_live_xxx --project-ref $PROJECT_REF
npx supabase secrets set WEBHOOK_SECRET=whsec_xxx --project-ref $PROJECT_REF
# Set multiple secrets at once
npx supabase secrets set API_KEY=value1 SIGNING_KEY=value2 --project-ref $PROJECT_REF
# List current secrets (names only, values hidden)
npx supabase secrets list --project-ref $PROJECT_REF
# Remove a secret
npx supabase secrets unset OLD_KEY --project-ref $PROJECT_REF
Step 2 — Zero-Downtime Deployments and Blue/Green Branching
Use Supabase database branching to test migrations against a production-like environment before cutting over.
Blue/green deployment via da
Implement custom role-based access control via JWT claims in Supabase: app_metadata.
Supabase Enterprise RBAC
Overview
Supabase supports custom role-based access control (RBAC) by storing role information in appmetadata on the user's JWT, then reading those claims in RLS policies via auth.jwt() ->> 'role'. This skill implements a complete RBAC system: roles in appmetadata, RLS policies that enforce role hierarchies, organization-scoped access, role management through the Admin API, and API endpoints protected with role checks — all using real createClient from @supabase/supabase-js.
When to use: Building multi-role applications (admin/editor/viewer), implementing organization-scoped access, creating custom permission systems beyond Supabase's built-in anon/authenticated roles, or scoping API operations by user role.
Prerequisites
@supabase/supabase-jsv2+ with service role key for admin operations- Understanding of JWT claims and Supabase's
auth.jwt()SQL function - Database access via SQL Editor or
psqlfor RLS policy creation - Supabase project with authentication configured
Instructions
The workflow has three steps: assign roles into the JWT, enforce them in the database with RLS, then enforce them again in application code.
Step 1: Define Roles via app_metadata and JWT Claims
Store custom roles in the user's app_metadata using the Admin API. These claims appear in every JWT the user receives and are readable in RLS policies. Assign roles with the service-role client:
// Define the role hierarchy
type AppRole = 'admin' | 'editor' | 'viewer' | 'member';
// Assign a role to a user (admin operation, service role key required)
async function setUserRole(userId: string, role: AppRole, orgId: string) {
const { data, error } = await supabase.auth.admin.updateUserById(userId, {
app_metadata: { role, org_id: orgId },
});
if (error) throw new Error(`Failed to set role: ${error.message}`);
return data.user;
}
Read the role back in app code with supabase.auth.getUser() and compare it against a numeric hierarchy so hasRole('editor', 'viewer') is true. See role assignment and JWT extraction for the full service-role client setup, granular permissions, bulk assignTeamRoles(), getCurrentUserRole(), getCurrentOrg(), hasRole(), and the requireRole() guard.
Step 2: RLS Policies with JWT Role Claims
Write Row Level Security policies that read auth.jwt() -> 'appmetadata' ->> 'role' and ... ->> 'orgid'. Wrap the JWT extraction in helper functions so every policy stays readable:
"Run your first Supabase query \u2014 insert a row and read it back.
Supabase Hello World — First Query
Overview
Execute your first real Supabase query: create a todos table in the dashboard, insert a row with the JS client, and read it back. This validates that your project URL, anon key, and Row Level Security are configured correctly before you build anything else.
Prerequisites
- Completed
supabase-install-authsetup (project URL + anon key in.env) @supabase/supabase-jsv2+ installed (npm install @supabase/supabase-js)- A Supabase project at supabase.com/dashboard
Instructions
The workflow is three steps: create the table (dashboard SQL), insert a row
with the JS client, and read it back to prove the round-trip. The skeleton
below is enough to follow along; the fully annotated code for every step —
including the error-branch comments and expected console output — lives in
the full implementation walkthrough.
Step 1: Create the todos Table
In the Supabase dashboard SQL Editor, create the table, enable Row Level
Security (required for anon-key access), and add permissive read + insert
policies for the hello-world exercise:
create table public.todos (
id bigint generated always as identity primary key,
task text not null,
is_complete boolean default false,
inserted_at timestamptz default now()
);
alter table public.todos enable row level security;
create policy "Allow public read" on public.todos
for select using (true);
create policy "Allow public insert" on public.todos
for insert with check (true);
Lock these policies down before production. Verify the table appears under
Table Editor before continuing.
Step 2: Insert a Row
Create the client from your env vars, then insert. Chain .select() to get
the inserted row back — .insert() alone returns { data: null }:
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
)
const { data, error } = await supabase
.from('todos')
.insert({ task: 'Hello from Supabase!' })
.select()
Step 3: Read It Back
Select all rows and confirm the row you inserted is present:
const { data: todos } = await supabase.from('todos').select('*')
// [{ id: 1, task: "Hello from Supabase!", is_complete: false, ... }]
Open the Table Editor in the dashboard to visually confirm the row is
there. See
>-. A structured response for Supabase-backed application failures. Work three layers in order: triage platform vs. application, run database diagnostics, then debug RLS, Edge Functions, and storage — ending with an evidence bundle for support escalation. When to use: Production errors involving Supabase, degraded API response times, connection pool exhaustion, silent data filtering from RLS, Edge Function cold start failures, or storage upload/download errors. Each step below gives the workflow plus a first command. The complete copy-paste blocks for every step live in Determine whether the issue is a Supabase platform incident or an application-level bug. Check the official status page first, then verify SDK client connectivity. Use If the status page is green, run the SDK small green platform plus a failing health check points at your queries, RLS, or Edge Functions — the SDK block, incident-check query, and full decision tree are in Connect directly via Supabase Incident Runbook
Overview
pgstatactivityPrerequisites
@supabase/supabase-js v2+ installed in your projectpsql diagnostics)Instructions
Step 1: Triage — Platform vs. Application
Read to inspect the app's Supabase env config and Grep to scan application logs for HTTP error codes (401=auth, 429=rate limit, 500=server).
# Check official status page — is this a platform-wide incident?
curl -sf https://status.supabase.com/api/v2/status.json | jq '.status'
# Expected: { "indicator": "none", "description": "All Systems Operational" }
healthCheck() (a select 1 against ahealthcheck table) to measure latency and confirm connectivity. AStep 2: Database Diagnostics with pgstatactivity
psql (or the Supabase SQL Editor) to inspect connections, find stuck queries, and detect leaks.
-- Current connections grouped by state — the first thing to run
SELECT state, count(*) AS connections,
max(extract(epoch FROM age(now(), state_change)))::int AS max_idle_seconds
FROM pg_stat_activity
WHERE datname = current_database()
GROUP BY state ORDER BY connections DESC;
-- WARNING: If idle > 20 or idl
'Install and configure Supabase SDK, CLI, and project authentication.
Supabase Install & Auth
Overview
Install the Supabase SDK, CLI, and project credentials from scratch — covering package install, environment configuration, client initialization, and connection verification for both TypeScript (@supabase/supabase-js) and Python (supabase).
Key facts:
- npm package:
@supabase/supabase-js - Python package:
supabase(via pip) - Client factory:
createClient()— nevernew SupabaseClient() - Dashboard: https://supabase.com/dashboard (Settings > API for keys)
- Docs: https://supabase.com/docs
Prerequisites
- Node.js 18+ (for JS/TS) or Python 3.8+ (for Python)
- Package manager: npm, pnpm, or yarn (JS) / pip (Python)
- A Supabase project created at https://supabase.com/dashboard
- Docker Desktop (only if using local development via
supabase start)
Instructions
Step 1 — Install the SDK and CLI
Install the SDK and the Supabase CLI:
JavaScript / TypeScript:
# Install the SDK
npm install @supabase/supabase-js
# For SSR frameworks (Next.js, SvelteKit, Nuxt), also install:
npm install @supabase/ssr
# Install the Supabase CLI (for types, migrations, local dev)
npm install -D supabase
Python:
# Install the SDK
pip install supabase
# Install the CLI (alternative: brew install supabase/tap/supabase)
npm install -g supabase
Verify the CLI is available:
npx supabase --version
Step 2 — Configure Environment Variables
Retrieve project credentials from the Supabase Dashboard (Settings > API) and create the env file:
# .env.local (or .env)
SUPABASE_URL=https://<project-ref>.supabase.co
SUPABASE_KEY=eyJhbGciOiJIUzI1NiIs... # anon key — safe for client-side
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1... # admin key — server-side ONLY
Add env files to .gitignore immediately:
.env
.env.local
.env.*.local
Security rules:
- The anon key (
SUPABASE_KEY) is safe for client-side code. It respects Row Level Security (RLS) policies. - The service role key (
SUPABASESERVICEROLE_KEY) bypasses RLS entirely. Use only on the server. Never bundle into client code or expose in browser.
Step 3 — Initialize the Client and Verify
Create a client singleton and verify connectivity:
TypeScript — client-side (anon key):
// lib/supabaseUse when reviewing Supabase code, onboarding developers, auditing an existing project, or debugging unexpected behavior — catches the twelve most common Supabase mistakes: exposing the service_role key in client bundles, forgetting to enable RLS, skipping connection pooling in serverless, .
Supabase Known Pitfalls
Overview
The twelve most common Supabase mistakes, ranked by severity: security (service_role exposure, missing RLS, permissive policies, no connection pooling), data integrity (ignoring { data, error }, missing .select() after mutations, .single() on optional results), and performance / maintainability (select('*'), N+1 queries, missing FK indexes, multiple client instances, no generated types). Each pitfall shows the broken code, why it fails, and the correct pattern using createClient from @supabase/supabase-js.
This SKILL.md carries the full pitfall table plus one representative fix per category. The verbatim broken-vs-correct code and detection queries for all twelve live in references/pitfalls.md — drill in there for depth.
Prerequisites
- Access to a Supabase project codebase for review
@supabase/supabase-jsv2+ installed- Basic understanding of Row Level Security (RLS)
Instructions
Work the pitfalls top-down by severity. Fix every Critical finding before moving on — a single security miss can expose the whole database.
| # | Pitfall | Severity | Fix | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | service_role key in client bundle | Critical | anon key on client; servicerole server-only, no NEXTPUBLIC_ |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 2 | Table without RLS | Critical | ALTER TABLE … ENABLE ROW LEVEL SECURITY right after CREATE TABLE |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 3 | Overly permissive RLS policy | Critical | scope USING (…) to auth.uid(), never USING (true) for writes |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 4 | No connection pooling in serverless | Critical | pooled string (Supavisor, port 6543), not the direct 5432 URL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 5 | Ignoring { data, error } |
High | destructure both; check error before touching data |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 6 | Missing .select() after mutation |
High | chain .select('cols') — mutations return null otherwise |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 7 | .single() on optional result |
High | use .maybeSingle() for 0-or-1; .single() only for guaranteed 1 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 8 | select('*') everywhere |
Medium | name the columns — smaller payload, typed, no leakage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 9 | N+1 query loop | Medium | PostgREST embedded join, or batch with .in() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 10 | FK column wit
supabase-load-scale
View full skill →
>-.
ReadWriteEditBash(supabase:*)Bash(psql:*)Bash(curl:*)Grep
Supabase Load & ScaleOverviewSupabase scaling operates at six layers: read replicas (offload analytics and reporting queries), connection pooling (Supavisor, the pgBouncer replacement, with transaction/session modes), compute upgrades (vCPU/RAM tiers), CDN for Storage (cache public bucket assets at the edge), **Edge Function regions (deploy functions closer to users), and table partitioning** (split billion-row tables for query performance). This skill gives the high-level workflow inline and links to
Prerequisites
InstructionsStep 1 — Read Replicas and Connection PoolingRoute read-heavy queries (dashboards, reports, search) to a read replica while keeping writes on the primary. Supavisor pools connections in two modes: transaction (default, port 6543 — shares connections, no prepared statements) and session (port 5432 — one connection per client, needed for prepared statements and and a read-only replica pointed at the
Then send analytics queries to Replicas lag slightly (typically <100ms), so never read-after-write on a replica. Full client config, pooled services, and a pool-usage monitoring query: read replicas and connection pooling. Step 2 — Compute Upgrades, CDN for Storage, and Edge Function RegionsMatch compute tier to traffic (Micro 60 connections → 4XL 480), serve public Storage buckets through the CDN with long content-addressed paths for cache busting, and deploy Edge Functions to the region closest to users:
supabase-local-dev-loop
View full skill →
'Configure Supabase local development with the CLI, Docker, and migration.
ReadWriteEditBash(npx:*)Bash(supabase:*)Bash(docker:*)Bash(curl:*)
Supabase Local Dev LoopOverviewRun the full Supabase stack locally — Postgres, Auth, Storage, Realtime, Edge Functions, and Studio — using Docker and the Supabase CLI. Local development mirrors production APIs exactly, enabling offline work, fast iteration, and repeatable migration workflows. Schema changes flow through Prerequisites
InstructionsStep 1: Initialize Project and Start Local Stack
This creates a
Start the local stack (first run pulls Docker images — takes a few minutes):
The CLI prints all local endpoints and keys:
Create
Verify the stack is running:
Step 2: Create Migrations and Seed DataCreate a migration file with a descriptive name:
Write the migration SQL:
supabase-migration-deep-dive
View full skill →
Database migration patterns with the Supabase CLI: npx supabase migration new, zero-downtime migrations, data backfill strategies, schema versioning, rollback strategies, and TypeScript type generation.
ReadWriteEditBash(npx supabase:*)Bash(supabase:*)Bash(psql:*)
Supabase Migration Deep DiveOverviewSupabase migrations are timestamped SQL files managed by the CLI that track schema changes across environments. This skill covers the full lifecycle — creating migrations, zero-downtime schema changes, batch backfills, versioning, rollback, and TypeScript type generation — using real CLI commands and When to use: Creating new database migrations, modifying production schemas without downtime, backfilling existing data after adding columns, managing migration history across dev/staging/production, rolling back failed migrations, or regenerating TypeScript types. Prerequisites
InstructionsStep 1: Create and Manage MigrationsCreate each migration as a timestamped SQL file, write the DDL, test it against a local reset, then promote it through environments with
Write DDL that enables RLS, adds policies, indexes, and triggers in the same file so the schema is complete when applied. For the full worked migration (a Step 2: Zero-Downtime Migration PatternsProduction schema changes must avoid locking tables. The core rules: adding a nullable column with a default is lock-free on Postgres 11+; build indexes with
supabase-multi-env-setup
View full skill →
Configure Supabase across development, staging, and production with separate projects, environment-specific secrets, and safe migration promotion.
ReadWriteEditBash(npx supabase:*)Bash(supabase:*)Bash(vercel:*)
Supabase Multi-Environment SetupOverviewProduction Supabase deployments require a separate project per environment — each with its own URL, API keys, database, and RLS policies. This skill configures a three-tier architecture (local dev, staging, production) with safe migration promotion via
for preview deployments, and CI/CD that prevents accidental cross-environment operations. When to use: setting up a new project with multiple environments, migrating from a single-project setup, adding staging to an existing dev/prod split, or configuring preview environments with database branching. Prerequisites
InstructionsThe workflow is three steps. Each step below gives the shape and the one command that matters; the full implementation walkthrough carries the complete env files, TypeScript client factory, RLS policies, CI/CD workflow, and seed data verbatim. Step 1: Environment Files and Project LayoutKeep one Supabase CLI project with shared migrations and one environment. Each file points at a different Supabase project; only commit.
The CLI links one project at a time. Before any re-link to the target:
See the implementation walkthrough (Step 1) for full Step 2: Environment-Aware Client and SafeguardsDetect the active environment once, then build browser (anon key, respects RLS) and server (service-role key, bypasses RLS) clients from it. Gate every destructive helper b
supabase-observability
View full skill →
Set up monitoring and observability for Supabase projects using Dashboard reports, CLI inspect commands, pg_stat_statements, log drains, and alerting.
ReadWriteEditBash(npx supabase:*)Bash(supabase:*)Grep
Supabase ObservabilityOverviewMonitor Supabase projects end-to-end: Dashboard reports for API/database/auth metrics, Follow the three steps below from this file for the high-level workflow, then drill into the linked Prerequisites
InstructionsStep 1: Dashboard Reports and CLI Inspect CommandsStart with the built-in Dashboard > Reports (API Requests, Database, Auth Usage, Storage, Realtime) for high-level metrics. For deeper Postgres diagnostics, use the
The full report table plus all nine inspect subcommands (table sizes, index usage, long-running queries, bloat, blocking, replication slots, and more) are in references/cli-inspect-commands.md. Step 2: Query Analytics with pgstatstatements and Log DrainsEnable
Forward logs to external aggregation with a log drain:
The complete slow-query / high-frequency / connection-monitoring SQL set and every log-drain command (Datadog, webhook, list, remove) are in references/query-analytics-and-log-drains.md. Step 3: Custom Metrics, Alerting, and Health ChecksEmit business-level metrics from a scheduled Edge Function, alert on quota thresholds, and expose a health endpoint for
supabase-performance-tuning
View full skill →
'Optimize Supabase query performance with indexes, EXPLAIN ANALYZE, connection.
ReadWriteEditBash(npx:supabase)Bash(supabase:*)Grep
Supabase Performance TuningOverviewSystematically improve Supabase query and database performance across three layers: PostgreSQL engine (indexes, query plans, materialized views), Supabase infrastructure (Supavisor connection pooling, Edge Functions, read replicas), and client SDK patterns (column selection, pagination, RPC functions). Every technique here is measurable — run Prerequisites
InstructionsStep 1: Diagnose — Find What Is SlowStart every performance effort with data. Enable Enable the stats extension:
Find the slowest queries by average execution time:
Check index usage and cache hit rates with the Supabase CLI:
Inspect active connections for pooling issues:
If Step 2: Indexes and Query PlansIndexes are the single highest-impact optimization. Use Read a query plan:
Look for Create a basic index:
supabase-policy-guardrails
View full skill →
'Enforce organizational governance for Supabase projects: shared RLS.
ReadWriteEditBash(supabase:*)Bash(psql:*)Bash(npx:*)Grep
Supabase Policy GuardrailsOverviewOrganizational governance for Supabase at scale: a shared RLS policy library (reusable templates for common access patterns), naming conventions (tables, columns, functions, policies), migration review process (CI checks ensuring RLS, preventing destructive operations, enforcing naming), cost alert configuration (billing thresholds and usage monitoring), and security audit scripts (scanning for exposed keys, missing RLS, overly permissive policies). All patterns use real Prerequisites
InstructionsStep 1 — Shared RLS Policy Library and Naming ConventionsRLS Policy TemplatesCreate reusable RLS policy templates that teams apply to new tables. This prevents each developer from writing ad-hoc policies and ensures consistent access control.
supabase-prod-checklist
View full skill →
Execute a Supabase production deployment checklist covering RLS, key hygiene, connection pooling, backups, monitoring, Edge Functions, and Storage policies.
ReadWriteEditBash(npx supabase:*)Bash(curl:*)Grep
Supabase Production Deployment ChecklistOverviewActionable 14-step checklist for taking a Supabase project to production, based on Supabase's official production guide. Each step below carries its verification checklist inline; the full SQL, TypeScript, and CLI commands for every step live in Prerequisites
InstructionsWork top to bottom. Every checkbox must be satisfied before go-live. Each step names the commands to run; copy them from Step 1: Enforce Row Level Security on ALL TablesRLS is the single most critical production requirement. Without it, any client with your anon key can read/write every row. Start with the audit query — it must return zero rows before going live:
Then full
Step 2: Enforce Key Separation — Anon vs Service RoleThe entirely and must never leave server-side environments. See the two-client setup in step-commands.md.
Step 3: Configure Connection Pooling (Supavisor)
supabase-rate-limits
View full skill →
Manage Supabase rate limits and quotas across all plan tiers.
ReadWriteEditBash(supabase:*)Bash(node:*)Bash(npx:*)Grep
Supabase Rate LimitsOverviewSupabase enforces rate limits and quotas across every API surface — PostgREST, Auth, Storage, Realtime, and Edge Functions — and the numbers scale by plan tier. This skill gives you the exact per-tier limits, connection pooling via Supavisor, retry/backoff and pagination patterns, and dashboard monitoring so you stay within quota and handle 429 errors gracefully. Prerequisites
InstructionsStep 1 — Know your tier limitsRate limits differ per surface and per plan. The headline API limits:
Auth, Storage, Realtime, Edge Functions, and Database connections each carry their own quotas. See the full per-surface breakdown in rate-limit-tiers.md before you architect. Step 2 — Pool connections with SupavisorSupavisor is Supabase's built-in connection pooler (replaced PgBouncer). Pick the mode by workload:
Transaction mode (port 6543) returns a connection to the pool after each transaction — the right default for serverless. Session mode (port 5432) holds a dedicated connection for LISTEN/NOTIFY and prepared statements. Full client setup and connection-string formats are in implementation.md. Step 3 — Retry, paginate, and batchWrap queries in an exponential-backoff retry that recognizes 429s and pool exhaustion:
Then cut request volume two ways: paginate large reads with
supabase-reference-architecture
View full skill →
"Implement enterprise Supabase reference architectures \u2014 monorepo\.
ReadWriteEditBash(npm:*)Bash(npx:*)Bash(supabase:*)Grep
Supabase Reference ArchitectureOverviewProduction Supabase applications need more than a flat For the full monorepo directory layout and microservices cross-project access, see Project Structure. For edge functions, caching, queue, and audit trail patterns, see Operational Patterns. Prerequisites
InstructionsStep 1: Client Singleton — The FoundationEvery app in the monorepo imports from a shared package instead of creating its own client. This guarantees a single source of truth for the URL, keys, and type definitions.
Key detail: The admin clien
supabase-reliability-patterns
View full skill →
'Build resilient Supabase integrations: circuit breakers wrapping createClient.
ReadWriteEditBash(supabase:*)Bash(curl:*)Grep
Supabase Reliability PatternsOverviewProduction Supabase apps need six reliability layers: circuit breakers (stop calling Supabase when it's down to prevent cascading failures), offline queue (buffer writes when the network is unavailable and replay when reconnected), graceful degradation (serve cached or fallback data during outages), health checks (detect Supabase availability before routing traffic), retry with exponential backoff (handle transient errors without overwhelming the service), and dual-write (write critical data to both Supabase and a backup store). All patterns use real Prerequisites
InstructionsStep 1 — Circuit Breaker and Retry with Exponential BackoffA circuit breaker tracks failures per Supabase service (database, auth, storage) and stops making calls when a threshold is exceeded. Combined with retry logic, it prevents both cascading failures and unnecessary retries during extended outages. Circuit Breaker Implementation
supabase-schema-from-requirements
View full skill →
'Design Supabase Postgres schema from business requirements with migrations,.
ReadWriteBash(supabase:*)Bash(npx:*)
Supabase Schema from RequirementsOverviewTranslate business requirements into a production-ready Postgres schema inside Supabase, covering the full path from specification to applied migration: entity extraction, tables with proper types and constraints, Row Level Security policies, performance indexes, timestamp triggers, and TypeScript type generation. Getting this right early matters because every downstream feature — auth, storage, realtime, edge functions — depends on well-designed tables. Prerequisites
InstructionsStep 1: Parse Requirements and Create MigrationRead the requirements document and extract entities, attributes, relationships, and access control rules. Map each entity to a Postgres table. Entity extraction example (project management app):
Create the migration file:
Write the migration SQL using standard Postgres data types ( Full worked migration (project-management app — tables, FKs, indexes, Data type selection guide:
The anon key is a JWT that PostgREST uses to determine which RLS policies apply. It is safe to include in client-side bundles — it can only access data that RLS policies explicitly allow. The service role key bypasses every RLS policy and should only ever exist in server-side code (API routes, Edge Functions, cron jobs, migration scripts).
Key rotation: Regenerate keys in Dashboard > Settings > API. After rotation, update every environment variable and redeploy all services. Old keys are invalidated immediately — there is no grace period. Step 2 — Enforce Row Level Security on Every Table
supabase-upgrade-migration
View full skill →
"Upgrade Supabase SDK and CLI versions with breaking-change detection\.
ReadWriteEditBash(npm:*)Bash(npx:*)Bash(pip:*)Bash(supabase:*)
Supabase Upgrade MigrationOverviewUpgrade Current State! ! ! Prerequisites
InstructionsStep 1: Audit Versions, Scan Usage, and Review Breaking ChangesCheck every installed Supabase package and find all import sites in the codebase.
supabase-js v1 → v2 breaking changes:
|