.claude/skills/supabase-rls-patterns/SKILL.md
Master Supabase Row Level Security (RLS) for RidenDine. Use when: (1) adding new tables, (2) modifying RLS policies, (3) debugging access control issues, (4) role-based data access. Key insight: All tables use RLS with role-based policies from profiles.role column.
npx skillsauth add Ritenoob/ridedine supabase-rls-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Supabase Row Level Security (RLS) controls who can see and modify database rows. RidenDine uses role-based RLS policies for 4 roles: customer, chef, driver, admin.
Use this skill when:
Role Hierarchy:
customer: Can see own orders, all approved chefs, all disheschef: Can manage own dishes, see assigned orders, update own profiledriver: Can see assigned deliveries, update delivery statusadmin: Elevated access for management (view all, manage chefs/drivers)Pattern: All policies check auth.uid() (current user's ID) against foreign keys or profiles.role.
-- Customers can only see their own orders
CREATE POLICY "Customers can view own orders" ON orders
FOR SELECT
USING (auth.uid() = customer_id);
-- Chefs can manage own dishes
CREATE POLICY "Chefs can manage own dishes" ON dishes
FOR ALL
USING (
auth.uid() IN (
SELECT profile_id FROM chefs WHERE id = dishes.chef_id
)
);
-- Anyone can read approved chefs
CREATE POLICY "Public can view approved chefs" ON chefs
FOR SELECT
USING (status = 'approved');
-- Only chef can update own profile
CREATE POLICY "Chefs can update own profile" ON chefs
FOR UPDATE
USING (auth.uid() = profile_id);
-- Admins can manage chefs
CREATE POLICY "Admins can manage chefs" ON chefs
FOR ALL
USING (
auth.uid() IN (
SELECT id FROM profiles WHERE role = 'admin'
)
);
-- Drivers can see assigned deliveries
CREATE POLICY "Drivers can view assigned deliveries" ON deliveries
FOR SELECT
USING (
driver_id IS NOT NULL AND
auth.uid() IN (
SELECT profile_id FROM drivers WHERE id = deliveries.driver_id
)
);
Step-by-step:
Enable RLS:
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
Add SELECT policy for each role:
CREATE POLICY "role_can_view" ON table_name
FOR SELECT
USING (condition_based_on_role);
Add INSERT/UPDATE/DELETE policies:
CREATE POLICY "role_can_insert" ON table_name
FOR INSERT
WITH CHECK (condition);
Test with different roles:
-- Simulate user context
SELECT set_config('request.jwt.claims', '{"sub": "user-uuid", "role": "customer"}', TRUE);
SELECT * FROM table_name; -- Should respect RLS
Cause: RLS policy blocking access
Fix:
SELECT auth.uid();SELECT * FROM profiles WHERE id = auth.uid();backend/supabase/migrations/Cause: Policy condition too restrictive
Fix:
ALTER TABLE table_name DISABLE ROW LEVEL SECURITY;ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;Cause: Missing policy for operation (INSERT/UPDATE/DELETE)
Fix:
WITH CHECK for INSERT/UPDATE-- Create table
CREATE TABLE favorites (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
favoritable_type TEXT NOT NULL CHECK (favoritable_type IN ('chef', 'dish')),
favoritable_id UUID NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE favorites ENABLE ROW LEVEL SECURITY;
-- Users can view own favorites
CREATE POLICY "Users can view own favorites" ON favorites
FOR SELECT
USING (auth.uid() = user_id);
-- Users can add favorites
CREATE POLICY "Users can add favorites" ON favorites
FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Users can delete own favorites
CREATE POLICY "Users can delete own favorites" ON favorites
FOR DELETE
USING (auth.uid() = user_id);
After adding RLS policies:
Test with Supabase client:
const { data, error } = await supabase.from('table_name').select();
if (error) console.error('RLS blocked:', error.message);
Test each role:
Check policies in Supabase dashboard:
backend/supabase/migrations/backend/supabase/migrations/20240101000000_init.sqldevelopment
Integrate Coinbase crypto payments into payment systems. Use when: (1) adding crypto payment support, (2) building onchain features, (3) implementing wallet functionality. Covers Coinbase Commerce (payment processor) vs CDP (developer platform), Server Wallets, Embedded Wallets, and multi-network support.
development
Add Apple Pay and Google Pay to Stripe checkout. Use when: (1) adding mobile wallet payments, (2) improving mobile conversion, (3) implementing one-tap checkout. Stripe Payment Request Button automatically detects device capabilities and shows Apple Pay (Safari/iOS) or Google Pay (Chrome/Android).
development
Master Vercel deployment for RidenDine web and admin Next.js apps. Use when: (1) deploying to production, (2) configuring environment variables, (3) setting up preview deployments, (4) debugging build failures, (5) configuring domains, (6) seeing "No Next.js version detected" error in Vercel builds, (7) setting up monorepo with separate projects on free tier. Key insight: Vercel monorepos require Root Directory configuration via dashboard (not vercel.json), GitHub integration auto-detects monorepo structure, free tier allows multiple projects.
tools
Master Supabase database migrations for RidenDine. Use when: (1) creating new migrations, (2) modifying schema, (3) adding RLS policies, (4) rolling back changes, (5) deploying to production. Key insight: Migrations are SQL files in backend/supabase/migrations/ executed in lexicographical order. Use supabase CLI to generate timestamps and apply migrations.