Choose Theme

Test Supabase RLS Policies

· 3 min read · #Supabase #TypeScript
--

Quickly test Row Level Security policies locally without manual database queries.

test-rls.ts
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL!;
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY!;
// Test with anonymous user (public access)
const anonClient = createClient(supabaseUrl, supabaseAnonKey);
// Test with authenticated user
const authClient = createClient(supabaseUrl, supabaseAnonKey);
async function testRLS() {
console.log('🔐 Testing RLS Policies...\n');
// Test 1: Anonymous read
console.log('1️⃣ Testing anonymous read access...');
const { data: anonData, error: anonError } = await anonClient
.from('posts')
.select('*');
if (anonError) {
console.error('❌ Anonymous read failed:', anonError.message);
} else {
console.log(`✅ Anonymous can read ${anonData.length} posts`);
}
// Test 2: Sign in and test auth read
console.log('\n2️⃣ Testing authenticated read access...');
const { data: authData } = await authClient.auth.signInWithPassword({
password: 'password123',
});
if (authData.user) {
const { data: userPosts, error: userError } = await authClient
.from('posts')
.select('*');
if (userError) {
console.error('❌ Auth read failed:', userError.message);
} else {
console.log(`✅ Authenticated can read ${userPosts.length} posts`);
}
}
// Test 3: Write access
console.log('\n3️⃣ Testing write access...');
const { error: writeError } = await authClient
.from('posts')
.insert({ title: 'Test Post', content: 'Test content' });
if (writeError) {
console.error('❌ Write failed:', writeError.message);
} else {
console.log('✅ Write succeeded');
}
// Test 4: Update own vs others
console.log('\n4️⃣ Testing update policies...');
const { error: updateError } = await authClient
.from('posts')
.update({ title: 'Updated' })
.eq('author_id', authData.user?.id);
if (updateError) {
console.error('❌ Update own post failed:', updateError.message);
} else {
console.log('✅ Can update own posts');
}
// Clean up
await authClient.auth.signOut();
console.log('\n✅ RLS tests complete!');
}
testRLS().catch(console.error);

Run with:

Terminal window
npx tsx test-rls.ts

Output:

🔐 Testing RLS Policies...
1️⃣ Testing anonymous read access...
✅ Anonymous can read 5 posts
2️⃣ Testing authenticated read access...
✅ Authenticated can read 12 posts
3️⃣ Testing write access...
✅ Write succeeded
4️⃣ Testing update policies...
✅ Can update own posts
✅ RLS tests complete!

Bonus: Automated test suite

interface PolicyTest {
name: string;
test: () => Promise<boolean>;
}
const tests: PolicyTest[] = [
{
name: 'Anonymous can read published posts',
test: async () => {
const { data, error } = await anonClient
.from('posts')
.select('*')
.eq('published', true);
return !error && data.length > 0;
},
},
{
name: 'Anonymous cannot read drafts',
test: async () => {
const { data, error } = await anonClient
.from('posts')
.select('*')
.eq('published', false);
return !error && data.length === 0;
},
},
// Add more tests...
];
for (const test of tests) {
const passed = await test.test();
console.log(passed ? '' : '', test.name);
}

When to use:

  • Before deploying RLS changes
  • Debugging permission issues
  • CI/CD testing
  • Local development validation

Related