Skip to content
Vibe Coding Best Practices
Database & Backend Safety12 min read

Supabase RLS basics for vibe-coded apps

A practical introduction to Supabase Row Level Security for AI-built apps that need to keep user and tenant data separated.

Intermediatesupabase db lintsupabase db diffrg "from\(|select\(|service_role|anon"

Supabase makes it very easy to build a real app quickly.

That speed is why vibe coders love it.

It is also why database access rules get skipped.

If your app stores user data, account data, payments, private content, uploaded files, API usage, chat history or team records, you need to understand Row Level Security.

What RLS does

Row Level Security controls which rows a user can read, insert, update or delete.

Without it, a query might return more data than the current user should see.

In a multi-user app, that can mean:

  • one user sees another user's records
  • one team accesses another team's workspace
  • public visitors can read private rows
  • authenticated users can update records they do not own
  • deleted or draft content leaks through broad selects

RLS is not a nice-to-have. It is a data boundary.

The dangerous default

The risky pattern looks like this:

Frontend uses Supabase client -> queries table directly -> table has weak or missing RLS

If the frontend can query a table directly, the table policies must enforce access correctly.

Do not rely on "the UI only asks for the current user's rows."

Users can change client-side requests.

Ask for a table inventory

Start by listing every table and what access it needs.

Use this prompt:

Review this Supabase database as a security reviewer.

Create a table-by-table access matrix.

For each table, say:
- what data it stores
- whether it contains personal, private, payment, or tenant-scoped data
- who should be able to select rows
- who should be able to insert rows
- who should be able to update rows
- who should be able to delete rows
- whether RLS should be enabled
- what policies are needed

This forces the conversation away from "does the query work?" and toward "who should be allowed to see this?"

Owner-only access

The common policy shape is owner-only access.

In plain English:

A user can read and update rows where user_id equals their authenticated user ID.

The exact SQL depends on your schema, but the idea is stable.

Ask:

Write owner-only RLS policies for tables that contain user-owned data.

Use the existing user ID column names from the schema.
Include select, insert, update and delete policies separately.
Explain what each policy allows and what it blocks.

Do not accept generated policies blindly. Read them.

Check that they reference the correct user column and do not accidentally allow all authenticated users.

Team or tenant access

Team apps are more complex.

Rows may belong to an organisation, workspace, project or account. Users may have roles inside that tenant.

The policy needs to answer:

  • is the current user a member of this tenant?
  • what role do they have?
  • can that role perform this action?
  • what happens when membership is revoked?

Use this prompt:

Design RLS policies for a multi-tenant app.

Use membership tables rather than trusting client-supplied organisation IDs.
Separate owner, admin, member and read-only access.
Explain how each policy prevents one tenant from reading or modifying another tenant's rows.

Service role keys

Supabase service role keys bypass RLS.

That is useful on trusted servers.

It is catastrophic in the browser.

Search:

rg "service_role|SUPABASE_SERVICE_ROLE|anon"

Ask:

Find every Supabase key usage in this app.

Confirm that anon keys only appear where safe, and service role keys are only used server-side in trusted code paths.
Flag any key that could be bundled into the browser or exposed through logs.

Watch for Supabase-specific traps

There are a few Supabase details that are easy to miss:

  • Do not use user-editable metadata for authorization decisions.
  • Views can bypass RLS unless configured carefully.
  • Updating rows usually also needs a matching select policy.
  • Storage upserts may need insert, select and update permissions.
  • Security definer functions should not live in an exposed schema.

Ask:

Review this Supabase setup for common security traps.

Check:
- whether authorization depends on user-editable metadata
- whether views should use security_invoker or be moved out of exposed schemas
- whether update policies also have the select access they need
- whether storage policies support the intended upload and replace behaviour
- whether security definer functions are exposed to anon or authenticated roles

These details are not theoretical. They are the kind of configuration mistakes that make an app look fine in the UI while the database boundary is weaker than expected.

Server-side queries still need discipline

Moving database calls to the server helps, but it does not remove the need for authorization.

Server routes must still check:

  • who is making the request
  • what account or tenant they belong to
  • whether they can access the requested record
  • whether the response returns only required fields

Ask:

Review server-side Supabase queries.

Find any route that:
- trusts an ID from the client without checking ownership
- returns entire rows when the UI only needs a few fields
- uses service role access unnecessarily
- does not handle missing or unauthorized records distinctly

Verify with negative tests

Do not only test your own account.

Create two test users:

  • User A owns record A
  • User B owns record B

Then try:

  • User A reading record B
  • User A updating record B
  • logged-out user reading private rows
  • normal member performing admin action

The expected result is denial.

If you never test the denied path, you do not know whether the boundary exists.

What PageLens can help with

PageLens can inspect public and authenticated website surfaces, and authenticated scans help reveal logged-in UX, trust and security signals.

RLS itself is a database-level control, so you still need schema and policy review.

Use PageLens to find what is visible from the app surface. Use Supabase policy review to prove users cannot cross data boundaries underneath it.

Supabase RLS basics for vibe-coded apps | PageLens AI