Skip to main content
API rules provide declarative, expression-based access control for your PocketBase collections. They determine who can list, view, create, update, and delete records through the API.

Overview

Each collection has five API rule types:
  • ListRule - Controls access to list/search operations
  • ViewRule - Controls access to viewing individual records
  • CreateRule - Controls who can create new records
  • UpdateRule - Controls who can update existing records
  • DeleteRule - Controls who can delete records
collection.ListRule = types.Pointer("@request.auth.id != ''")
collection.ViewRule = types.Pointer("")
collection.CreateRule = types.Pointer("@request.auth.id != ''")
collection.UpdateRule = types.Pointer("@request.auth.id = author")
collection.DeleteRule = types.Pointer("@request.auth.id = author")

Rule syntax

API rules use a simple expression language:
// Allow everyone
collection.ListRule = types.Pointer("")

// Deny everyone
collection.ListRule = nil

// Conditional access
collection.ListRule = types.Pointer("published = true")
Rules use nil (not set) to deny all access, empty string "" to allow all access, and expressions for conditional access.

Request context

Rules have access to request information via @request:

Authentication

// Check if user is authenticated
"@request.auth.id != ''"

// Check user ID
"@request.auth.id = 'USER_ID'"

// Check user field
"@request.auth.role = 'admin'"
"@request.auth.verified = true"
"@request.auth.email = 'admin@example.com'"

Request data

// Access submitted data (for Create/Update rules)
"@request.data.published = true"
"@request.data.title != ''"
"@request.data.price > 0"

Request method

// Check HTTP method
"@request.method = 'GET'"
"@request.method != 'DELETE'"

Query parameters

// Access URL query parameters
"@request.query.status = 'active'"
"@request.query.page = '1'"

Headers

// Access request headers
"@request.headers.x-custom-header = 'value'"

Record fields

Rules can reference record fields directly:
// Simple field comparison
"published = true"
"status = 'active'"
"price > 100"

// Date comparisons
"created > '2024-01-01'"
"expires < @now"

// Array contains
"tags ?= 'featured'"
"roles ?= 'admin'"

Operators

Comparison operators

"count = 10"      // Equal
"count != 5"      // Not equal
"price > 100"     // Greater than
"price >= 100"    // Greater than or equal
"price < 50"      // Less than
"price <= 50"     // Less than or equal

Logical operators

"published = true && featured = true"  // AND
"status = 'active' || status = 'pending'"  // OR
"!(archived = true)"  // NOT

String operators

"title ~ 'hello'"     // Contains (case-sensitive)
"title ~~ 'hello'"    // Contains (case-insensitive)
"email ?~ '.+@.+'"    // Regex match
"email !~ 'test'"     // Not contains

Array operators

"tags ?= 'featured'"   // Array contains
"tags ?!= 'hidden'"    // Array not contains
"roles ?~ 'admin|mod'" // Array contains (regex)

Relation operators

"author.role = 'admin'"              // Relation field access
"category.name = 'Technology'"       // Nested relation
"comments.user.verified = true"      // Deep relation
"tags.id ?= @request.auth.favoriteTag"  // Relation with auth

Special variables

@now

Current date/time:
"created > @now - 86400"  // Created in last 24 hours
"expires < @now"          // Already expired
"startDate <= @now && endDate >= @now"  // Currently active

@todayStart / @todayEnd

Today’s date boundaries:
"created >= @todayStart"  // Created today
"created < @todayStart"   // Created before today

@monthStart / @monthEnd

Current month boundaries:
"created >= @monthStart"  // Created this month

@yearStart / @yearEnd

Current year boundaries:
"created >= @yearStart"   // Created this year

Common patterns

Public read, authenticated write

collection.ListRule = types.Pointer("")
collection.ViewRule = types.Pointer("")
collection.CreateRule = types.Pointer("@request.auth.id != ''")
collection.UpdateRule = types.Pointer("@request.auth.id != ''")
collection.DeleteRule = types.Pointer("@request.auth.id != ''")

Owner-only access

collection.ListRule = types.Pointer("@request.auth.id != '' && author = @request.auth.id")
collection.ViewRule = types.Pointer("author = @request.auth.id")
collection.CreateRule = types.Pointer("@request.auth.id != ''")
collection.UpdateRule = types.Pointer("author = @request.auth.id")
collection.DeleteRule = types.Pointer("author = @request.auth.id")

Published content

collection.ListRule = types.Pointer(
    "published = true || @request.auth.id = author",
)
collection.ViewRule = types.Pointer(
    "published = true || @request.auth.id = author",
)
collection.UpdateRule = types.Pointer("@request.auth.id = author")
collection.DeleteRule = types.Pointer("@request.auth.id = author")

Admin override

collection.UpdateRule = types.Pointer(
    "@request.auth.id = author || @request.auth.role = 'admin'",
)
collection.DeleteRule = types.Pointer(
    "@request.auth.id = author || @request.auth.role = 'admin'",
)

Verified users only

collection.CreateRule = types.Pointer(
    "@request.auth.id != '' && @request.auth.verified = true",
)

Prevent self-deletion

collection.DeleteRule = types.Pointer(
    "@request.auth.id != '' && @request.auth.id != id",
)

Rule validation

Validate rules before saving:
// Rules are validated automatically during collection save
err := app.Save(collection)
if err != nil {
    // Handle validation error
}

Testing rules

Test rules programmatically:
// Check if user can access record
canAccess, err := app.CanAccessRecord(
    record,
    requestInfo,
    collection.ViewRule,
)

CreateRule specifics

CreateRule has special behavior for owner fields:
// Auto-set author field
collection.CreateRule = types.Pointer(
    "@request.auth.id != '' && @request.data.author = @request.auth.id",
)
This ensures users can only create records assigned to themselves.

UpdateRule specifics

UpdateRule evaluates against the original record state:
// User can update their own records
collection.UpdateRule = types.Pointer("author = @request.auth.id")

// Prevent changing ownership
collection.UpdateRule = types.Pointer(
    "author = @request.auth.id && @request.data.author = author",
)

// Prevent status changes
collection.UpdateRule = types.Pointer(
    "@request.auth.id = author && @request.data.status = status",
)

Performance considerations

Create database indexes for fields used in API rules to improve query performance.
Deep relation traversals (e.g., post.author.team.organization.owner) can be slow. Keep rules simple when possible.
Specific rules like author = @request.auth.id are faster than complex OR conditions.
For CreateRule and UpdateRule, validate submitted data efficiently by checking required fields first.

Security best practices

Begin with strict rules (nil) and gradually relax them as needed. It’s easier to grant access than to remove it.
Always verify record ownership in UpdateRule and DeleteRule to prevent unauthorized modifications.
Use field-level visibility settings alongside API rules to hide sensitive data.
Test all rule combinations with different user roles and authentication states.
Log and review changes to API rules, especially for production collections.
Manage rules grant elevated permissions. Apply them conservatively and validate admin status properly.
Ensure ListRule and ViewRule don’t expose records users shouldn’t see, even if they guess IDs.

Advanced examples

Multi-tenant system

collection.ListRule = types.Pointer(
    "@request.auth.id != '' && @request.auth.organizationId = organizationId",
)
collection.CreateRule = types.Pointer(
    "@request.auth.id != '' && " +
    "@request.data.organizationId = @request.auth.organizationId",
)

Time-based access

collection.ViewRule = types.Pointer(
    "publishDate <= @now && (expiryDate = '' || expiryDate >= @now)",
)

Role-based permissions

collection.CreateRule = types.Pointer(
    "@request.auth.id != '' && @request.auth.roles ?= 'editor'",
)
collection.DeleteRule = types.Pointer(
    "@request.auth.id != '' && (" +
    "  @request.auth.roles ?= 'admin' ||" +
    "  (@request.auth.roles ?= 'editor' && author = @request.auth.id)" +
    ")",
)

Collaborative editing

collection.UpdateRule = types.Pointer(
    "@request.auth.id != '' && (" +
    "  author = @request.auth.id ||" +
    "  collaborators ?= @request.auth.id" +
    ")",
)

Draft vs published

collection.ListRule = types.Pointer(
    "(published = true) || " +
    "(@request.auth.id != '' && author = @request.auth.id)",
)
collection.ViewRule = types.Pointer(
    "published = true || " +
    "(@request.auth.id != '' && author = @request.auth.id)",
)

Debugging rules

Enable query logging to debug rule evaluation:
app.OnRecordListRequest().Bind(&hook.Handler[*core.RecordListRequestEvent]{
    Func: func(e *core.RecordListRequestEvent) error {
        e.App.Logger().Debug(
            "List request",
            "collection", e.Collection.Name,
            "rule", cast.ToString(e.Collection.ListRule),
            "auth", e.Auth != nil,
        )
        return e.Next()
    },
})
Use the PocketBase Admin UI to test API rules interactively. The “API Preview” feature shows exactly how rules affect API responses.