Demo, all content is generated
Question

Soft delete with RLS: do I really need `deleted_at is null` in every query?

Open · 336 views · asked by pixelpaulo · edited

I want an undo for deleted projects. Plan: deleted_at timestamptz column. But now every select in my Lovable app needs .is('deleted_at', null) and I know I will forget one somewhere. Is there a way to hide deleted rows by default?

What I’ve tried

Asked Lovable, it added the filter to 14 places. Missed two already.

Comment

3 answers

hannah_reyes · edited

Put it in the select policy, then no query can forget it:

create policy "own active projects" on public.projects
  for select to authenticated
  using ((select auth.uid()) = owner_id and deleted_at is null);

Catch: once the row is soft deleted, it's invisible to its owner, so update ... set deleted_at = null for undo finds nothing. Do the undo through a small security definer function that checks ownership and restores it, or add a separate policy that lets owners see their own deleted rows only from a "trash" page (e.g. a view with security_invoker).

Comment
The undo catch is exactly what would have bitten me tomorrow. pixelpaulo · edited
katja_s · edited

Different take: a projects_active view with security_invoker = true and where deleted_at is null, and the app only reads from the view. Keeps the policy simple and the trash page can read the table. Both work, it's taste.

Comment
Agree, the view is cleaner if the app code is yours. With Lovable writing queries I'd still put it in the policy so it can't be bypassed by accident. hannah_reyes · edited
oksana_k · edited

Also add a partial index on projects (owner_id) where deleted_at is null, otherwise the filter gets slower as the trash grows.

Comment