Supabase Row Level Security — Protect Your Database Row by Row
In this tutorial, you will learn about Supabase Row Level Security. We cover key concepts, practical examples, and best practices to help you master this topic.
Supabase Row Level Security (RLS) uses PostgreSQL policies to restrict which rows users can read, write, update, or delete based on their authentication status and custom conditions.
What You'll Learn
By the end of this lesson you will enable RLS on tables, write security policies for all CRUD operations, use auth.uid() for user-based access, and test policies with Supabase tools.
Why It Matters
RLS is Supabase's primary security mechanism. Without RLS, anyone with your anon key can read or write all your data. With RLS, each user sees only their own data, even with the same API key.
Real-World Use
DodaZIP users can only see their own ZIP files and processing history. The RLS policy checks auth.uid() = user_id on every query, preventing data leaks between tenants.
flowchart LR
U1[User A] -->|Query files| API[Supabase API]
U2[User B] -->|Query files| API
API -->|RLS Policy| DB[(PostgreSQL)]
DB -->|uid matches| U1
DB -->|uid matches| U2
style API fill:#3ecf8e,color:#fff
Enabling RLS
Every table must have RLS enabled before policies take effect.
-- Enable RLS on a table
ALTER TABLE files ENABLE ROW LEVEL SECURITY;
-- For new projects, RLS is enabled by default on all tables
-- Verify RLS status
SELECT relname, relrowsecurity
FROM pg_class
WHERE relname = 'files';
# rls_enable.py
# Understanding RLS enablement
def rls_checklist():
items = [
"ALTER TABLE files ENABLE ROW LEVEL SECURITY",
"Create SELECT policy for read access",
"Create INSERT policy for write access",
"Create UPDATE policy for modify access",
"Create DELETE policy for remove access",
"Test policies with anon key in browser",
"Test policies with authenticated user",
]
print("RLS Setup Checklist:")
for item in items:
print(f" [ ] {item}")
rls_checklist()
SELECT Policies
Control which rows users can read.
-- Allow users to read their own files
CREATE POLICY "Users can view own files"
ON files FOR SELECT
USING (auth.uid() = user_id);
-- Allow users to read any public files
CREATE POLICY "Users can view public files"
ON files FOR SELECT
USING (is_public = true);
-- Allow service role to read all files
CREATE POLICY "Service role can view all files"
ON files FOR SELECT
USING (auth.role() = 'service_role');
# select_policy.py
# Understanding SELECT policies
def explain_select_policy():
policy_sql = """
CREATE POLICY "Users can view own files"
ON files FOR SELECT
USING (auth.uid() = user_id);
"""
print("SELECT Policy Breakdown:")
print(f" Policy name: Users can view own files")
print(f" Table: files")
print(f" Operation: SELECT")
print(f" Condition: Only rows where auth.uid() equals user_id column")
print()
print("SQL:")
print(policy_sql)
explain_select_policy()
INSERT Policies
Control which rows users can create.
-- Allow users to insert their own files
CREATE POLICY "Users can create own files"
ON files FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Allow authenticated users to insert any file
CREATE POLICY "Authenticated users can insert"
ON files FOR INSERT
WITH CHECK (auth.role() = 'authenticated');
# insert_policy.py
# INSERT policy example
def write_insert_policy():
sql = """
CREATE POLICY "Users can insert own files"
ON files FOR INSERT
WITH CHECK (auth.uid() = user_id);
"""
print("INSERT Policy:")
print(" Ensures users can only create rows where user_id matches their UID")
print(" WITH CHECK validates the new row against the condition")
print()
print("Key difference from SELECT:")
print(" SELECT policies use USING (existing rows)")
print(" INSERT policies use WITH CHECK (new rows)")
write_insert_policy()
UPDATE and DELETE Policies
Control modification and removal of rows.
-- Allow users to update their own files
CREATE POLICY "Users can update own files"
ON files FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Allow users to delete their own files
CREATE POLICY "Users can delete own files"
ON files FOR DELETE
USING (auth.uid() = user_id);
# update_delete_policy.py
# UPDATE and DELETE policies
def write_update_policy():
print("UPDATE Policy Example:")
print()
print(" USING (auth.uid() = user_id) -- which rows can be updated")
print(" WITH CHECK (auth.uid() = user_id) -- what values can be set")
print()
print("DELETE Policy Example:")
print()
print(" USING (auth.uid() = user_id) -- which rows can be deleted")
print()
print("Note: DELETE only needs USING, not WITH CHECK")
write_update_policy()
Common Mistakes
Creating policies but forgetting to enable RLS: Policies have no effect until
ALTER TABLE ... ENABLE ROW LEVEL SECURITYis run.Using the wrong clause: SELECT and DELETE use
USINGonly. INSERT usesWITH CHECKonly. UPDATE uses both.Not testing with the anon key: Test policies by making requests without authentication headers to verify public access is correctly blocked.
Overly permissive policies: Starting with
USING (true)grants full access. Write restrictive policies first, then expand as needed.Forgetting auth.role() checks for service_role: If your backend uses the service_role key, it bypasses RLS. Write policies that explicitly handle this role.
Practice Questions
What does RLS stand for and what does it do? Row Level Security. It restricts which rows a user can access based on the policy conditions.
What SQL command enables RLS on a table?
ALTER TABLE table_name ENABLE ROW LEVEL SECURITY.What is the difference between USING and WITH CHECK? USING filters existing rows (SELECT, UPDATE, DELETE). WITH CHECK validates new or modified rows (INSERT, UPDATE).
How do you reference the current user in an RLS policy? Use
auth.uid()which returns the UUID of the authenticated user.Challenge: Write RLS policies for a multi-tenant application where users belong to organizations. Each user should see only their organization's data.
FAQ
Mini Project
Create RLS policies for a collaborative document application where:
- Users can read documents shared with them
- Users can create their own documents
- Document owners can update and delete their documents
- Document collaborators can edit shared documents
def document_rls_policies():
policies = [
"CREATE POLICY read_shared ON docs FOR SELECT USING (auth.uid() = owner_id OR auth.uid() IN (SELECT user_id FROM doc_collaborators WHERE doc_id = id))",
"CREATE POLICY create_own ON docs FOR INSERT WITH CHECK (auth.uid() = owner_id)",
"CREATE POLICY update_own ON docs FOR UPDATE USING (auth.uid() = owner_id OR auth.uid() IN (SELECT user_id FROM doc_collaborators WHERE doc_id = id AND role = 'editor'))",
"CREATE POLICY delete_own ON docs FOR DELETE USING (auth.uid() = owner_id)",
]
print("Document App RLS Policies:")
for p in policies:
print(f" {p}")
document_rls_policies()
What's Next
Next: Authentication for implementing user auth.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro