Skip to main content
This page is part of an AI coding agent skill and is written for agents, not humans. For the human-readable Base44 docs, see the developer documentation.

RLS Examples

Practical Row-Level Security patterns for common application types. Important: Base44 RLS supports:
  • Logical operators: $or, $and, $nor for combining conditions
  • Field operators (for data.* fields): $in, $nin, $ne, $all
  • user_condition: Equality only (no operators)

Contents


Simple Patterns (JSON Schema)

These patterns work with the JSON schema RLS format.

Todo App - Owner-only access

Users see and manage only their own tasks.
{
  "name": "Task",
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "description": { "type": "string" },
    "completed": { "type": "boolean" },
    "priority": { "type": "string", "enum": ["low", "medium", "high"] },
    "due_date": { "type": "string", "format": "date" }
  },
  "rls": {
    "create": true,
    "read": { "created_by": "{{user.email}}" },
    "update": { "created_by": "{{user.email}}" },
    "delete": { "created_by": "{{user.email}}" }
  }
}

Contact Form - Public create, admin-only read

Anyone can submit, only admins can view submissions.
{
  "name": "ContactSubmission",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string", "format": "email" },
    "message": { "type": "string" }
  },
  "rls": {
    "create": true,
    "read": { "user_condition": { "role": "admin" } },
    "update": { "user_condition": { "role": "admin" } },
    "delete": { "user_condition": { "role": "admin" } }
  }
}

User Profile - Self-management

Users can only access their own profile.
{
  "name": "UserProfile",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "avatar_url": { "type": "string" },
    "bio": { "type": "string" },
    "preferences": { "type": "object" }
  },
  "rls": {
    "create": true,
    "read": { "created_by": "{{user.email}}" },
    "update": { "created_by": "{{user.email}}" },
    "delete": { "created_by": "{{user.email}}" }
  }
}

Department Data - Same department access

Users can only see records from their department.
{
  "name": "DepartmentAnnouncement",
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "content": { "type": "string" },
    "department": { "type": "string" }
  },
  "rls": {
    "create": { "user_condition": { "role": "manager" } },
    "read": { "data.department": "{{user.data.department}}" },
    "update": { "user_condition": { "role": "manager" } },
    "delete": { "user_condition": { "role": "admin" } }
  }
}

Subscription - Admin-managed, user-readable via email field

{
  "name": "Subscription",
  "type": "object",
  "properties": {
    "user_email": { "type": "string" },
    "tier": { "type": "string", "enum": ["free", "basic", "pro", "enterprise"] },
    "credits": { "type": "number" },
    "renewal_date": { "type": "string", "format": "date" }
  },
  "rls": {
    "create": { "user_condition": { "role": "admin" } },
    "read": { "data.user_email": "{{user.email}}" },
    "update": { "user_condition": { "role": "admin" } },
    "delete": { "user_condition": { "role": "admin" } }
  }
}
Note: This pattern only allows users to read their own subscription. Admins need to use the Dashboard UI to configure additional read access for themselves.

Private Data - Owner-only

{
  "name": "PrivateNotes",
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "content": { "type": "string" },
    "tags": { "type": "array", "items": { "type": "string" } }
  },
  "rls": {
    "create": true,
    "read": { "created_by": "{{user.email}}" },
    "update": { "created_by": "{{user.email}}" },
    "delete": { "created_by": "{{user.email}}" }
  }
}

Public Read, Authenticated Write

Anyone can read, only logged-in users can create/edit their own records.
{
  "name": "BlogPost",
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "content": { "type": "string" },
    "author_email": { "type": "string" }
  },
  "rls": {
    "create": true,
    "read": true,
    "update": { "created_by": "{{user.email}}" },
    "delete": { "created_by": "{{user.email}}" }
  }
}

Using Operators

Logical Operators

Combine multiple conditions using $or, $and, or $nor: Owner OR Admin access:
{
  "name": "Document",
  "type": "object",
  "properties": {
    "title": { "type": "string" },
    "content": { "type": "string" }
  },
  "rls": {
    "create": true,
    "read": {
      "$or": [
        { "created_by": "{{user.email}}" },
        { "user_condition": { "role": "admin" } }
      ]
    },
    "update": {
      "$or": [
        { "created_by": "{{user.email}}" },
        { "user_condition": { "role": "admin" } }
      ]
    },
    "delete": { "user_condition": { "role": "admin" } }
  }
}
Multiple roles with $or:
{
  "rls": {
    "read": {
      "$or": [
        { "user_condition": { "role": "admin" } },
        { "user_condition": { "role": "manager" } },
        { "user_condition": { "role": "hr" } }
      ]
    }
  }
}

Field Operators for data.* Fields

Use $in, $nin, $ne, $all for comparing entity data fields: Access based on tags ($in):
{
  "rls": {
    "read": {
      "data.category": { "$in": ["public", "shared"] }
    }
  }
}
Exclude specific statuses ($nin):
{
  "rls": {
    "read": {
      "data.status": { "$nin": ["archived", "deleted"] }
    }
  }
}
Not equal ($ne):
{
  "rls": {
    "read": {
      "data.visibility": { "$ne": "private" }
    }
  }
}
All tags must match ($all):
{
  "rls": {
    "read": {
      "data.required_tags": { "$all": ["approved", "reviewed"] }
    }
  }
}

Combining Logical and Field Operators

{
  "rls": {
    "read": {
      "$and": [
        { "data.status": { "$ne": "draft" } },
        {
          "$or": [
            { "created_by": "{{user.email}}" },
            { "data.visibility": "public" }
          ]
        }
      ]
    }
  }
}

Field-Level Security Examples

Control access to specific fields within an entity.

Sensitive Salary Field

{
  "name": "Employee",
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string", "format": "email" },
    "salary": {
      "type": "number",
      "description": "Annual salary",
      "rls": {
        "read": { "user_condition": { "role": "hr" } },
        "write": { "user_condition": { "role": "hr" } }
      }
    },
    "performance_notes": {
      "type": "string",
      "description": "Manager notes",
      "rls": {
        "read": {
          "$or": [
            { "user_condition": { "role": "manager" } },
            { "user_condition": { "role": "hr" } }
          ]
        },
        "write": { "user_condition": { "role": "manager" } }
      }
    }
  }
}

Admin-Only Internal Fields

{
  "name": "Order",
  "type": "object",
  "properties": {
    "order_number": { "type": "string" },
    "total": { "type": "number" },
    "internal_notes": {
      "type": "string",
      "description": "Internal processing notes",
      "rls": {
        "read": { "user_condition": { "role": "admin" } },
        "write": { "user_condition": { "role": "admin" } }
      }
    },
    "profit_margin": {
      "type": "number",
      "description": "Profit margin percentage",
      "rls": {
        "read": { "user_condition": { "role": "admin" } },
        "write": false
      }
    }
  }
}

Complex Patterns (Dashboard UI or Backend)

Some patterns may still require the Dashboard UI or backend functions.

Bidirectional Relationships (e.g., Friendships, Matches)

Requirement: Either party in a relationship should have access. Now possible with $or:
{
  "rls": {
    "read": {
      "$or": [
        { "data.user_a_email": "{{user.email}}" },
        { "data.user_b_email": "{{user.email}}" }
      ]
    }
  }
}
Alternative solutions:
  1. Entity redesign: Store two records per relationship (one for each party)
  2. Backend function: Query with custom logic

Complex Business Logic

Requirement: Access depends on multiple entity fields with complex conditions. JSON Schema limitation: While operators help, very complex business logic may still be hard to express. Solution options:
  1. Backend function: Implement custom access logic
  2. Combine simpler rules: Break complex rules into simpler entity-level and field-level rules

Best Practices

Security Strategy

Use a combination of entity-level RLS and field-level security:
Data TypeApproachExample
User-editableEntity RLS: Owner-onlyUserProfile with created_by check
Sensitive fieldsField-level RLSSalary field with HR role check
Multi-role access$or with user_conditionAdmin OR Manager access
Conditional accessField operators$in, $ne on data fields
Public contentEntity RLS: read: truePublicPost
Private contentEntity RLS: Owner-onlyPrivateNote

When to Use Each Approach

RequirementApproach
Single condition (owner, admin, department)JSON Schema RLS
Multiple OR/AND conditionsJSON Schema RLS with $or/$and
Field value checks with $in/$ne/etc.JSON Schema RLS for data.* fields
Field-level access controlJSON Schema FLS (field-level rls)
Complex comparison operators ($gt, $lt)Backend functions
Very complex business logicBackend functions

Common Role Patterns

RoleTypical Access
adminFull access to all records
moderatorRead/update access, limited delete
managerDepartment-scoped access
userOwn records only

Supported Operators Summary

OperatorSupportedNotes
$orYesCombine multiple conditions
$andYesAll conditions must match
$norYesNone of the conditions match
$inYesFor data.* fields only
$ninYesFor data.* fields only
$neYesFor data.* fields only
$allYesFor data.* fields only
$gt, $lt, $gte, $lteNoUse backend functions
$regexNoUse backend functions

Limitations Summary

Not SupportedAlternative
Operators on user_conditionUse equality only for user checks
Comparison operators ($gt, $lt)Backend functions
Regex matching ($regex)Backend functions
Cross-entity relationshipsBackend functions