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'"
// 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" ,
)
Create database indexes for fields used in API rules to improve query performance.
Avoid complex relation chains
Deep relation traversals (e.g., post.author.team.organization.owner) can be slow. Keep rules simple when possible.
Use specific rules over wildcards
Specific rules like author = @request.auth.id are faster than complex OR conditions.
Optimize @request.data checks
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.
Use manage rules carefully
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.