How I Built Multi-User Apps with Row-Level Security in Supabase

How I Built Multi-User Apps with Row-Level Security in Supabase

1 4
calendar_todayschedule2 min read
— Originally published at www.clintech.me

Originally published on clintech.me

When you move from toy projects to real products, the hard problem
isn't styling cards. It's isolation: how do you make sure each user
only sees their own data?

The wrong answer: filter by userId in React and hope no one cheats.
The right answer: Row-Level Security.

What RLS actually is

RLS is a Postgres feature that enforces access policies at the database
level — not the application level. Every query runs through your
policies before returning data.

CREATE POLICY "Users can see their own jobs"
ON jobs FOR SELECT
USING (user_id = auth.uid());

With this in place, even if someone opens DevTools and calls the
Supabase REST API directly with a valid token, they still only get rows
where user_id matches their own auth UID. The database is the
security gate, not your frontend.

How I applied this in ApplyCraft

ApplyCraft is an AI-powered job
tracker I built. Every job, note, and profile is scoped per user.
The policy pattern is consistent across all tables:

  • jobs.user_id = auth.uid()
  • job_notes.user_id = auth.uid()
  • profiles.id = auth.uid()

Even the server-side AI endpoint, which writes outreach copy back
to a job row, verifies ownership before touching the database.
No client-side key exposure, no trust in the frontend.

The mistakes that will burn you

1. Enabling RLS but leaving "allow all" policies
You've technically turned it on but effectively disabled it.
Every policy needs to be explicit.

2. Forgetting write policies
Easy to add SELECT and forget INSERT, UPDATE, DELETE.
Your app will silently fail on writes and you'll spend an hour
wondering why.

3. Trusting only frontend filters
If a table has unrestricted SELECT, any user can bypass your
UI and call the REST endpoint directly. RLS removes that
possibility entirely.

4. Not following the ownership chain on joins
If notes belongs to jobs and jobs belongs to a user, your
note policies should check ownership through that chain, not
just assume the foreign key is enough.


The difference between "multi-user UI" and "multi-tenant product"
is where security lives. Once the database enforces it, your
frontend gets simpler and your app gets much harder to abuse.


I'm a frontend engineer open to remote EU roles, connect with
me on LinkedIn
if this was useful.

1 Comment

1 vote
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelski - Mar 19

SolidJS 2.0 Async Data: A Deep Dive for React Devs

morellodev - Jul 16

5 Web Dev Pitfalls That Are Silently Killing Your Projects (With Real Fixes)

Dharanidharan - Mar 3

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23
chevron_left
149 Points5 Badges
Ibadanclintech.me
1Posts
0Comments
Frontend Engineer | React • Next.js • TypeScript | I build fast, scalable interfaces for product-led startups | Open to Remote EU Roles

Related Jobs

View all jobs →

Commenters (This Week)

2 comments
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!