/supabase
Supabase tables with migrations, grants, RLS, typed clients and denied-access tests.
Then type /supabase in Claude Code, or describe the task and let it pick the skill.
What you get back contract tests · 9 Oct 2026
- 36 access-rule tests pass
- Magic-link sign-in added
- Nothing pushed to remote
- The dashboard now reads real flats, members and expenses from Supabase, and each signed-in user only sees the flats they belong to. Nothing has been pushed to the remote project, and nothing is committed.
- Verification. Docker isn't installed, so I couldn't run Supabase's local stack. Instead I applied the migration to a scratch Postgres 16 with a minimal stand-in for Supabase's auth schema and roles. My 36 access-rule tests (
supabase/tests/rls_test.sql) all pass there, the build passes, and signed-out visitors to/dashboardare redirected to/login. - Nobody can move an expense to another flat, promote themselves to owner, or pose as another user. The tests cover each of these, plus other users and people in no flat.
Input → output from the run
const expenses = [
{ date: "Oct 1", what: "Groceries — Nature's Basket", paidBy: "Arjun", amount: 2840.5, split: "4 ways", status: "Unsettled" },
{ date: "Sep 30", what: "Electricity bill (Sept)", paidBy: "Maya", amount: 3125, split: "4 ways", status: "Settled" },
{ date: "Sep 28", what: "Wi-Fi — ACT Fibernet", paidBy: "Kabir", amount: 1179, split: "4 ways", status: "Unsettled" },
{ date: "Sep 27", what: "Dinner at Toit", paidBy: "Arjun", amount: 4620, split: "3 ways", status: "Unsettled" },
{ date: "Sep 25", what: "Cooking gas refill", paidBy: "Neha", amount: 903, split: "4 ways", status: "Settled" },
];
export default function Dashboard() {
const total = expenses.reduce((s, e) => s + e.amount, 0);export default async function Dashboard({ searchParams }) {
const supabase = await createClient();
const { data: auth } = await supabase.auth.getClaims();
const userId = auth?.claims?.sub;
if (!userId) redirect("/login");
// RLS limits every query below to flats this user belongs to.
const { data: memberships, error: membershipsError } = await supabase
.from("group_members")
.select("id, group_id, groups(name)")
.eq("user_id", userId)
.order("created_at");
if (membershipsError) throw new Error(`Couldn't load your flats: ${membershipsError.message}`);When to use it
Use when integrating Supabase or repairing data access and tenant isolation.
Skip it if you are not on Supabase, or want the same four CRUD policies stamped on every table.
The thinking behind itMost Supabase bugs are access bugs. Get tables, policies and typed clients right from the start, and every user only ever sees their own data.
Output contract from the skill's Output section
Report tables/functions/storage changed, migration files, grants and RLS policies with auth strategy, client/session changes, type-generation command/result, negative authorization tests, and remaining linked-project/dashboard/production steps.
How it checks its own work from the instructions
- Local migrations reset and apply cleanly
- Schema and generated types show no drift
- Anonymous, other-user, and other-tenant access is denied
- Attempts to change row ownership are tested
Requirements what it needs
- Works in
- Codex, Claude Code, Cursor
- Needs
- project files, terminal commands, network access
The instructions SKILL.md · 32 lines
Instructions & references
Back to overview ↑On this page 4 sections
Supabase
Build a migration-backed, typed Supabase integration with explicit authorization boundaries.
Workflow
- Inspect
supabase/, migrations, generated types, installed Supabase packages/versions, framework/runtime, environment-variable names without printing values, auth/session flow, schemas exposed through the Data API, and existing grants/RLS. - Infer table ownership and tenant relationships from the domain model. Ask only when ownership, collaboration, deletion, retention, or external-auth behavior is ambiguous. Do not add
user_idor four CRUD policies to every table mechanically. - Design keys, foreign keys, constraints, indexes, timestamps, lifecycle/deletion behavior, and server-generated ownership defaults appropriate to each table. Use external provider subject IDs as text unless a local identity mapping is intentionally modeled.
- Create named migrations. Use
supabase migration newor a revieweddb difffor local changes, verify with a local reset/test database, inspectdb push --dry-run, and usedb pushonly when remote deployment is explicitly in scope.migration upis not a substitute for remote deployment. - Secure every exposed object with both Postgres grants and RLS. Scope policies to roles, use indexed predicates, add
with checkfor inserts/updates, review views/functions separately, and set a safesearch_pathonsecurity definerfunctions. - Match auth strategy:
- Supabase Auth: use
auth.uid()/auth.jwt()and current SSR session guidance. - Clerk or another supported third-party provider: configure the native integration, pass the provider token through the client, and use the actual JWT
sub/claims. Do not assume an RLS expression alone configures token verification. - Server administration: use a dedicated server-only client with explicit authorization; never expose secret/service credentials.
- Supabase Auth: use
- For current
@supabase/ssr, create a client per request, use the supported cookiegetAll/setAlladapter, await async framework cookie APIs, and add the required proxy/middleware refresh flow. Do not use legacy single-cookie adapters. - Generate TypeScript types after applying migrations to the target used for generation, then run type checks and update typed queries. Avoid
anyand handle{ data, error }explicitly.
Verification
Run local migration reset/apply, schema/type drift checks, and repository lint/type/test/build. For each policy, test allowed owner/member/admin/service access and denied anonymous/other-user/other-tenant reads and mutations, including attempts to change ownership. Test session refresh/expiry and external-provider tokens in the target runtime.
Output
Report tables/functions/storage changed, migration files, grants and RLS policies with auth strategy, client/session changes, type-generation command/result, negative authorization tests, and remaining linked-project/dashboard/production steps.
Claude app, ChatGPT and ZIP download
local coding agent required
This skill requires project files, terminal commands, and network access. Uploading it to a chat app does not provide equivalent execution.
ChatGPT Skills
This workflow needs a local coding environment or capabilities that a chat-only Skills upload does not provide.
Package details
- Package
- supabase/SKILL.md
- Author
- @tushaarmehtaa
- Added
- 16 Jun 2026
- Tags