Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/pocketbase/pocketbase/llms.txt

Use this file to discover all available pages before exploring further.

PocketBase uses rule-based access control to determine who can access and modify your data. Rules are written as filter expressions and applied at the collection level.

Rule types

Every collection (base, auth, or view) can have the following API rules:
RuleDescriptionAvailable On
List RuleControls who can list/search recordsAll collections
View RuleControls who can view individual recordsAll collections
Create RuleControls who can create recordsBase and auth collections
Update RuleControls who can update recordsBase and auth collections
Delete RuleControls who can delete recordsBase and auth collections
Auth RuleAdditional constraints for authenticationAuth collections only
Manage RuleAdmin-like permissions for auth recordsAuth collections only
Rules are evaluated using filter expressions, similar to the filter query parameter syntax.

Rule values

Each rule can have one of three values:
collection.ListRule = nil
Effect: Only superusers can perform this action. Regular users and guests are denied access.
Use nil to completely lock down an action. This is the most restrictive setting.

Auth rule

The authRule is special because it applies after successful authentication:
type collectionAuthOptions struct {
    // AuthRule could be used to specify additional record constraints
    // applied after record authentication and right before returning the
    // auth token response to the client.
    //
    // For example, to allow only verified users you could set it to
    // "verified = true".
    //
    // Set it to empty string to allow any Auth collection record to authenticate.
    //
    // Set it to nil to disallow authentication altogether for the collection
    // (that includes password, OAuth2, etc.).
    AuthRule *string `json:"authRule"`
}
Common use cases:
collection.AuthRule = types.Pointer("verified = true")
The auth rule check happens in RecordAuthResponse:
// From apis/record_helpers.go:51
ok, err := e.App.CanAccessRecord(authRecord, originalRequestInfo, authRecord.Collection().AuthRule)
if !ok {
    return firstApiError(err, e.ForbiddenError(
        "The request doesn't satisfy the collection requirements to authenticate.", err))
}

Manage rule

The manageRule gives admin-like permissions for auth records:
type collectionAuthOptions struct {
    // ManageRule gives admin-like permissions to allow fully managing
    // the auth record(s), eg. changing the password without requiring
    // to enter the old one, directly updating the verified state and email, etc.
    //
    // This rule is executed in addition to the Create and Update API rules.
    ManageRule *string `json:"manageRule"`
}
What manage rule allows:
  • Change passwords without requiring the old password
  • Directly update the verified state
  • Modify email without confirmation
  • Ignore email visibility settings when viewing records
Example configurations:
// Allow superusers only (default secure setting)
collection.ManageRule = nil

// Allow the user to manage their own record
collection.ManageRule = types.Pointer("@request.auth.id = id")

// Allow admin users to manage any record
collection.ManageRule = types.Pointer("@request.auth.role = 'admin'")

// Allow users to manage records in their organization
collection.ManageRule = types.Pointer("@request.auth.organizationId = organizationId")
The manage rule enables email visibility for matched records:
// From apis/record_helpers.go:462
if collection.ManageRule == nil || *collection.ManageRule == "" {
    return nil // no manage rule to check
}

// Fetch the ids of the managed records
resolver := core.NewRecordFieldResolver(app, collection, requestInfo, true)
expr, err := search.FilterData(*collection.ManageRule).BuildExpr(resolver)

// Ignore the email visibility check for the managed records
for _, id := range managedIds {
    if rec, ok := mappedRecords[id]; ok {
        rec.IgnoreEmailVisibility(true)
    }
}
The manage rule is executed in addition to the Create and Update API rules. Use it carefully as it grants elevated permissions.

Filter expression syntax

Rules use the same syntax as the filter query parameter:

Special fields

FieldDescriptionExample
@request.auth.idID of authenticated user@request.auth.id != ''
@request.auth.*Any field from auth record@request.auth.role = 'admin'
@request.methodHTTP method@request.method = 'GET'
@request.query.*Query parameters@request.query.page = '1'
@request.data.*Request body data@request.data.status = 'draft'
@request.headers.*Request headers@request.headers.x_custom != ''
@collection.*Reference another collection@collection.posts.id ?= authorId
@collection.* and @request.* fields are only available to superusers when used in filter query parameters for security reasons.

Operators

OperatorDescriptionExample
=Equalsstatus = 'active'
!=Not equalsrole != 'guest'
>Greater thanage > 18
>=Greater or equalcredits ≥ 100
<Less thanprice < 50
<=Less or equalquantity ≤ 10
~Contains (case-insensitive)name ~ 'john'
!~Not containsemail !~ '@spam.com'
?=Any of (array contains)tags ?= 'featured'
?!=None ofcategories ?!= 'archived'

Logical operators

// AND
"verified = true && status = 'active'"

// OR
"role = 'admin' || role = 'moderator'"

// Grouping
"(role = 'admin' || role = 'moderator') && verified = true"

Common rule patterns

Public read, authenticated write

collection.ListRule = types.Pointer("")   // Anyone can list
collection.ViewRule = types.Pointer("")   // Anyone can view
collection.CreateRule = types.Pointer("@request.auth.id != '") // Only authenticated users can create
collection.UpdateRule = types.Pointer("@request.auth.id = userId") // Only owner can update
collection.DeleteRule = types.Pointer("@request.auth.id = userId") // Only owner can delete

Owner-only access

rule := "@request.auth.id = userId"

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

Role-based access

// Admin can do everything
adminRule := "@request.auth.role = 'admin'"

// Members can read, admins can write
collection.ListRule = types.Pointer("@request.auth.id != ''")
collection.ViewRule = types.Pointer("@request.auth.id != ''")
collection.CreateRule = types.Pointer(adminRule)
collection.UpdateRule = types.Pointer(adminRule)
collection.DeleteRule = types.Pointer(adminRule)

Organization/tenant isolation

// Users can only access records in their organization
orgRule := "@request.auth.organizationId = organizationId"

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

Status-based access

// Published posts are public, drafts are owner-only
collection.ListRule = types.Pointer("status = 'published' || (@request.auth.id = userId && status = 'draft')")
collection.ViewRule = types.Pointer("status = 'published' || @request.auth.id = userId")
collection.CreateRule = types.Pointer("@request.auth.id != ''")
collection.UpdateRule = types.Pointer("@request.auth.id = userId")
collection.DeleteRule = types.Pointer("@request.auth.id = userId")

Rule validation

PocketBase validates rules during collection save:
// From core/collection_model_auth_options.go:151
validation.Field(
    &o.AuthRule,
    validation.By(cv.checkRule),
    validation.By(cv.ensureNoSystemRuleChange(cv.original.AuthRule)),
)
System collection rules cannot be changed:
// From core/collection_validate.go:517
if c.IsSystem() && c.hasSystemRuleChanges() {
    return validation.NewError(
        "validation_collection_system_rule_change",
        "System collection API rule cannot be changed."
    )
}
You cannot modify API rules for system collections like _superusers, _mfas, _otps, etc.

Checking access programmatically

You can check if a user can access a record using the CanAccessRecord method:
requestInfo, _ := e.RequestInfo()

ok, err := app.CanAccessRecord(record, requestInfo, collection.ViewRule)
if !ok {
    return errors.New("access denied")
}

List vs View rule differences

The key difference between List and View rules:
  • List Rule: Acts as a filter on the query results. Records not matching the rule are excluded from the list.
  • View Rule: Acts as a permission check. If the rule doesn’t match, the request is denied with a 403/404 error.
// List rule example
collection.ListRule = types.Pointer("status = 'published'")
// Result: Only published records are returned in list queries

// View rule example  
collection.ViewRule = types.Pointer("@request.auth.id = userId")
// Result: Returns 404 if user tries to view someone else's record
The List rule is applied as a query filter, while the View rule is a binary permission check.

Expansion and rules

When expanding relations, PocketBase applies the View rule of the related collection:
// From apis/record_helpers.go:381
if relCollection.ViewRule == nil {
    return fmt.Errorf("only superusers can view collection %q records", 
        relCollection.Name)
}

if *relCollection.ViewRule != "" {
    resolver := core.NewRecordFieldResolver(app, relCollection, requestInfoPtr, true)
    expr, err := search.FilterData(*(relCollection.ViewRule)).BuildExpr(resolver)
    if err != nil {
        return err
    }
    q.AndWhere(expr)
}

Security best practices

Always test your rules thoroughly. Incorrect rules can expose sensitive data or prevent legitimate access.

Best practices

  1. Start restrictive: Begin with nil rules and open up access as needed.
  2. Validate both sides: Check both @request.auth and record fields in rules.
  3. Use manage rule carefully: Only grant manage permissions to trusted users.
  4. Test as different users: Always test rules from the perspective of different user roles.
  5. Avoid public write access: Be very cautious about setting Create/Update/Delete rules to "".
  6. Consider cascading deletes: Ensure delete rules account for related records.
  7. Audit rule changes: Track changes to collection rules in production.

Custom rule validation

You can add custom rule validation using hooks:
app.OnCollectionValidate().Bind(&hook.Handler[*core.CollectionEvent]{
    Func: func(e *core.CollectionEvent) error {
        if e.Collection.IsAuth() && e.Collection.AuthRule == nil {
            log.Printf("Warning: Auth collection %s has authentication disabled",
                e.Collection.Name)
        }
        
        // Custom validation logic
        if e.Collection.Name == "sensitive_data" && 
           e.Collection.ListRule != nil && 
           *e.Collection.ListRule == "" {
            return errors.New("sensitive_data cannot have public list access")
        }
        
        return e.Next()
    },
})

Rule migration example

When migrating from v0.22 to later versions:
// From migrations/1717233556_v0.23_migrate.go:361
if collection.Name == core.CollectionNameSuperusers {
    collection.AuthRule = types.Pointer("verified=true")
} else {
    collection.AuthRule = types.Pointer("")
}

Collections

Learn about collection types

Auth overview

Authentication system overview

Query syntax

Filter expression reference

Hooks

Customize behavior with hooks