Build the mental model
Even if every API endpoint correctly checks permissions, what happens if one has a bug, a new endpoint forgets the check, or someone queries the database directly? Application-level authorization lives entirely in code a mistake can bypass.
Row-level security moves part of that enforcement into the database itself, as a policy attached to a table, so the database refuses to return rows a query is not allowed to touch — regardless of which code path asked.
Authentication does not imply row-level enforcement
Knowing who is connected does not, by itself, limit what rows that connection can see. Someone still has to write the policy tying identity to row ownership — RLS is a deliberate, additional layer, not something that comes free once users log in.
This lesson stays conceptual. PostgreSQL's roles-privileges-and-row-security lesson and the Cloud Providers & Platforms course's Supabase lesson both cover the real, hands-on policy syntax.
- Row-Level Security
- A database feature where access-control policies are attached directly to a table, so the database itself filters which rows a given connection can see or modify, based on who that connection is — independent of and in addition to application-level checks.
ROW-LEVEL SECURITY POLICY LAYER
-------------------------------
NOTES TABLE (raw rows) RLS POLICY LAYER
+----+-------+-----------+ (owner = current_user)
| id | owner | text |
+----+-------+-----------+
| 1 | alice | "shopping"| ---> User A (alice) queries:
| 2 | bob | "taxes" | sees only row 1, 3
| 3 | alice | "recipe" |
| 4 | carol | "diary" | ---> User B (bob) queries:
+----+-------+-----------+ sees only row 2
Same table, same query shape --
the policy layer decides what
each connection is allowed back.Connect it to a real scenario
Row-level security is most valuable exactly where application-level authorization is easiest to get wrong: multi-tenant apps, user-generated content, and any table where 'my own data' must stay strictly separate from everyone else's.
Application layer
Checks permissions before making a request — the first line of defense.
Database layer
Enforces row ownership as a second, independent guarantee that cannot be bypassed by a forgetful code path.
When they disagree
The database's policy wins, because it sits closer to the data.
The code below simulates this outside any real database: given rows tagged with an owner and a 'currently connected' user, it filters down to only the rows that user should see — a miniature of what a real RLS policy enforces automatically.
Try the working example
// Simulates what a database row-level-security policy would enforce:
// each connecting user only ever sees rows they own.
function applyRowLevelSecurity(rows, currentUser) {
return rows.filter((row) => row.owner === currentUser);
}
const notes = [
{ id: 1, owner: "alice", note: "Alice's private note" },
{ id: 2, owner: "bob", note: "Bob's private note" },
{ id: 3, owner: "alice", note: "Alice's second note" },
{ id: 4, owner: "carol", note: "Carol's private note" }
];
console.log("alice sees:", applyRowLevelSecurity(notes, "alice"));
console.log("bob sees:", applyRowLevelSecurity(notes, "bob"));Filtering the four-row notes table for "alice" returns her two rows (ids 1 and 3), and filtering for "bob" returns only his one row (id 2) — carol's row never appears for either user, exactly mimicking how a real row-level-security policy would scope each connection's results.5-minute try-it
Add a fifth row { id: 5, owner: "carol", note: "Carol's second note" } to the notes array, then call applyRowLevelSecurity(notes, "carol"). How many rows come back, and why does neither alice's nor bob's filtered result ever include a carol row, no matter how the array is reordered?
One important caution
Assuming that requiring users to log in automatically limits what data they can query — authentication and row-level authorization are separate layers that must both be built.
Relying on row-level security as the only safeguard and skipping application-level permission checks, losing the benefit of having two independent layers.
Wikipedia: Row-level security — How Databases Work