Security Rules (RLS)
Overview
Section titled “Overview”Security rules let you define Row Level Security (RLS) policies for your PostgreSQL tables directly in your collection definitions. When the Drizzle schema is generated, Rebase creates the corresponding CREATE POLICY statements.
const postsCollection: EntityCollection = { slug: "posts", dbPath: "posts", properties: { /* ... */ }, securityRules: [ { operation: "select", access: "public" }, { operations: ["insert", "update", "delete"], ownerField: "author_id" } ]};How It Works
Section titled “How It Works”- You define
securityRuleson a collection rebase schema generatecreates Drizzle schema with RLS enabledrebase db pushorrebase db migrateapplies the policies to PostgreSQL- Every query is filtered by the current user’s context automatically
The authenticated user’s identity is available in SQL via:
| Function | Returns |
|---|---|
auth.uid() | The current user’s ID |
auth.roles() | Comma-separated app role IDs |
auth.jwt() | Full JWT claims as JSONB |
These are set automatically per-transaction by the Rebase backend.
Convenience Shortcuts
Section titled “Convenience Shortcuts”Owner-based Access
Section titled “Owner-based Access”The simplest pattern — users can only access rows they own:
securityRules: [ { operation: "all", ownerField: "user_id" }]This generates: USING (user_id = auth.uid())
Public Access
Section titled “Public Access”Allow anyone (including unauthenticated users) to read:
securityRules: [ { operation: "select", access: "public" }]This generates: USING (true)
Authenticated Access
Section titled “Authenticated Access”Allow any authenticated user:
securityRules: [ { operation: "select", access: "authenticated" }]Role-based Access
Section titled “Role-based Access”Restrict operations to specific roles:
securityRules: [ { operation: "all", roles: ["admin"] }, { operation: "select", roles: ["editor", "viewer"] }]Raw SQL Expressions
Section titled “Raw SQL Expressions”For complex logic, use using and withCheck:
securityRules: [ { operation: "select", using: "EXISTS (SELECT 1 FROM org_members WHERE org_members.org_id = {org_id} AND org_members.user_id = auth.uid())" }]using— Filters which existing rows are visible (applies to SELECT, UPDATE, DELETE)withCheck— Validates new row values (applies to INSERT, UPDATE)
Column references use {column_name} syntax which gets resolved to the full table-qualified column.
Combining Shortcuts and SQL
Section titled “Combining Shortcuts and SQL”Mix convenience shortcuts with raw SQL:
securityRules: [ // Admins can do anything { operation: "all", roles: ["admin"], using: "true" }, // Regular users can only see their own rows { operation: "select", ownerField: "user_id" }, // Users can insert, but only for themselves { operation: "insert", withCheck: "{user_id} = auth.uid()" }, // Locked rows cannot be updated { operation: "update", mode: "restrictive", using: "{is_locked} = false" }]Permissive vs Restrictive
Section titled “Permissive vs Restrictive”PostgreSQL has two policy modes:
- Permissive (default) — Multiple permissive policies are OR’d together. If any one passes, access is granted.
- Restrictive — Restrictive policies are AND’d together. All must pass.
securityRules: [ // Permissive: owners can access their rows { operation: "all", ownerField: "user_id" }, // Restrictive: but locked rows cannot be updated { operation: "update", mode: "restrictive", using: "{is_locked} = false", withCheck: "{is_locked} = false" }]Operations
Section titled “Operations”| Operation | SQL Equivalent | Description |
|---|---|---|
"select" | SELECT | Read rows |
"insert" | INSERT | Create new rows |
"update" | UPDATE | Modify existing rows |
"delete" | DELETE | Remove rows |
"all" | All of the above | Shorthand for all operations |
You can also use operations (plural) to apply one rule to multiple operations:
{ operations: ["insert", "update", "delete"], ownerField: "author_id" }Full SecurityRule Interface
Section titled “Full SecurityRule Interface”interface SecurityRule { name?: string; // Human-readable policy name operation?: SecurityOperation; // Single operation operations?: SecurityOperation[]; // Multiple operations mode?: "permissive" | "restrictive"; // Default: "permissive" access?: "public" | "authenticated"; ownerField?: string; // Column containing the owner user ID roles?: string[]; // App roles that this policy applies to using?: string; // Raw SQL USING expression withCheck?: string; // Raw SQL WITH CHECK expression}Examples
Section titled “Examples”Blog Platform
Section titled “Blog Platform”securityRules: [ // Anyone can read published posts { operation: "select", access: "public", using: "{status} = 'published'" }, // Authors can see their own drafts { operation: "select", ownerField: "author_id" }, // Authors can create and edit their own posts { operations: ["insert", "update"], ownerField: "author_id" }, // Only admins can delete { operation: "delete", roles: ["admin"] }]Multi-Tenant SaaS
Section titled “Multi-Tenant SaaS”securityRules: [ { operation: "all", using: "EXISTS (SELECT 1 FROM org_members WHERE org_members.org_id = {org_id} AND org_members.user_id = auth.uid())" }]Next Steps
Section titled “Next Steps”- Relations — Foreign keys and joins
- Entity Callbacks — Lifecycle hooks