| name | chravel-supabase-rls |
| description | Audit and implement Supabase Row-Level Security policies, edge functions, and auth integration for Chravel. Use when modifying database access patterns, adding tables, writing RLS policies, or debugging permission issues. Triggers on "RLS", "row level security", "supabase policy", "edge function", "permission denied", "auth issue with supabase". |
Chravel Supabase RLS & Edge Functions
Supabase is Chravel's backend. Every data access pattern must respect RLS.
Architecture
Key Files
src/integrations/supabase/client.ts — Supabase singleton client
src/integrations/supabase/ — Generated types, query helpers
- Database types generated by Supabase CLI
Auth Model
- Supabase Auth handles user identity
auth.uid() is the only trusted source of user identity
- User IDs from client params, localStorage, or URL are NEVER trusted
RLS Principles
1. Trip Access
- Trip existence ≠ trip access
- Users can only access trips they are members of
- Trip membership must be verified server-side (RLS policy on
trip_members)
- Demo trips may have special public access rules
2. Policy Patterns
CREATE POLICY "Users can view trip data"
ON trip_items FOR SELECT
USING (
EXISTS (
SELECT 1 FROM trip_members
WHERE trip_members.trip_id = trip_items.trip_id
AND trip_members.user_id = auth.uid()
)
);
CREATE POLICY "Members can insert"
ON trip_items FOR INSERT
WITH CHECK (
EXISTS (
SELECT 1 FROM trip_members
WHERE trip_members.trip_id = trip_items.trip_id
AND trip_members.user_id = auth.uid()
)
);
3. Critical Rules
- Never bypass RLS with
service_role key in client code
- Never trust client-provided
user_id — always use auth.uid()
- Auth state must resolve BEFORE data fetching
- Loading ≠ Not Found ≠ Empty — distinguish these states in UI
Edge Functions
- Used for server-side operations requiring elevated privileges
- Must validate auth tokens server-side
- Must apply business logic checks before mutations
- Error responses must be structured and informative
Common Issues
Trip Not Found Regression
The #1 Chravel regression. Causes:
- Auth not resolved before trip query → race condition
- Missing trip member check → access denied looks like "not found"
- Effect dependency missing auth state → stale query runs
Prevention:
- Auth gate component wraps all trip routes
- Trip hook waits for auth hydration before querying
- Error state distinguishes "loading" from "not found" from "not authorized"
Audit Checklist