FOR SECURE APPS

Just F*cking Use RLS.

You're spending hours writing complex middleware, checking permissions in every controller, and praying you didn't miss a single `where` clause. Stop that shit. Use the database.

Your Backend is a Mess.

You have 500 endpoint files, 20 custom auth helpers, and you're still not sure if a malicious user can edit someone else's profile by guessing an ID. This is not how you build secure software.

This is your current workflow:

// In your API route...
const { id } = req.body;
const user = await db.getUser(req.userId);
if (user.role !== 'admin' && user.id !== id) {
  return res.status(403).send('F*ck off');
}
// 10 lines of security code for 2 lines of logic.
await db.updateData(id, data);

You do this for EVERY. SINGLE. ENDPOINT. It's a disaster waiting to happen. You'll miss one. You always do.

What the f*ck is RLS?

Row Level Security is a Postgres feature that lets you define access rules in the database schema. When enabled, your database automatically filters rows based on the current user.

Your application code becomes this:

// No middleware, no checks, just logic.
const { data, error } = await supabase
  .from('profiles')
  .update({ name: 'New Name' })
  .eq('id', userId);

The database does the heavy lifting. If the user doesn't own the row, the update simply fails. Period.

Why it's f*cking great

1. Proximity to Data

Security stays next to the data. If you change your API, change your framework, or connect via a different tool, the security rules still apply.

2. Declarative Rules

Describe who can see what, instead of writing how to filter every query. It's readable, maintainable, and audit-friendly.

3. Performance

Postgres is optimized for this. When done correctly with indexes and proper helper functions, the overhead is negligible compared to the network latency of your bloated middleware.

Real World Patterns

Stop overthinking. These 3 patterns cover 90% of your needs.

User-Owned Rows

The classic: each row belongs to a single user. Perfect for profiles, todos, and private notes.

-- Enable RLS
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see their own profiles
CREATE POLICY "Users can view own profile" 
ON profiles FOR SELECT 
TO authenticated 
USING ( (select auth.uid()) = id );

Multi-Tenant SaaS (org_id)

Used for team-based apps where multiple users in one organization share data.

-- Policy: Users see data from their organization
CREATE POLICY "Team isolation" 
ON projects FOR ALL 
TO authenticated 
USING ( 
  org_id IN (
    SELECT org_id FROM user_orgs 
    WHERE user_id = (select auth.uid())
  ) 
);

Admins Manage Everything

Combine user ownership with admin privileges in a single rule.

-- Policy: Owner + Admin Hybrid
CREATE POLICY "Owner or Admin access" 
ON documents FOR ALL 
TO authenticated 
USING ( 
  auth.uid() = user_id 
  OR 
  (select auth.jwt()->>'role') = 'admin'
);

Soft Deletes

Hide deleted rows from normal users, but keep them in the DB for auditing.

-- Policy: Show only non-deleted rows
CREATE POLICY "Hide soft-deleted" 
ON posts FOR SELECT 
TO authenticated 
USING ( deleted_at IS NULL );

-- Policy: Admins see everything
CREATE POLICY "Admins see deleted" 
ON posts FOR SELECT 
TO authenticated 
USING ( (select auth.jwt()->>'role') = 'admin' );

Don't F*ck Up Your Security.

Auditing RLS manually is a nightmare. Are you sure you didn't leave a table wide open? Are your policies optimized for performance?

SupaExplorer is the auditing platform for Supabase teams. We scan your database, identify leaks, and give you AI-powered recommendations in seconds.