Functions and configuration
Row-level security
Row-level security (RLS) lets PostgreSQL decide which rows each user can read or change. The database enforces it, so it applies to every API call, SDK, and realtime subscription.
How it fits with API rules
Tables have API rules for list, view, create, update, and delete. RLS policies run beneath those rules, inside PostgreSQL. Use both: API rules for the simple cases, and RLS as a second layer that holds even if a rule is too loose.
Turn on RLS and add a policy
- Open your Project and select Row-level security.
- Pick a table and switch it from RLS off to RLS on. With no policies, nobody except the service role can read the table.
- Select New policy and give it a name, such as Owners can read their orders.
- Choose the command: SELECT, INSERT, UPDATE, DELETE, or all.
- Choose the roles: Anonymous, Authenticated, or Service role.
- Write the USING expression, which decides which existing rows the user can see or change.
- For INSERT and UPDATE, write the WITH CHECK expression, which decides which new values are allowed.
- Select Preview SQL to review the statement, then Save policy.
Common policies
| Goal | Command | Expression |
|---|---|---|
Users read only their own rows | SELECT | USING owner = auth.uid() |
Users create rows for themselves | INSERT | WITH CHECK owner = auth.uid() |
Users edit only their own rows | UPDATE | USING owner = auth.uid(), WITH CHECK owner = auth.uid() |
Everyone reads published posts | SELECT | USING status = 'published' |
Test before you ship
Use Test access with a real test user ID to see exactly which rows that user can reach. Try an anonymous visitor, a signed-in user, the owner, and an administrator.
await rb.admin.rls.setEnabled("orders", { enabled: true });
await rb.admin.rls.createPolicy("orders", {
name: "owners_read",
command: "SELECT",
roles: ["authenticated"],
using: "owner = auth.uid()",
});
const result = await rb.admin.rls.test({ table: "orders", userId: "USER_ID" });Good practice
- Start with everything denied and add the smallest access each group needs.
- Store administrator roles in their own table, never on the user's profile where users could edit them.
- Do not query a protected table from its own policy. Use a helper function for role checks instead, or the policy can loop.
- Index the columns your policies filter on, such as owner.
Fix it yourself. The link below opens this file in GitHub's editor and forks the repository for you if you need one, and your change becomes a pull request without leaving the browser.