API rules and filter expressions for PocketBase access control. Use when setting permissions, writing filter expressions, configuring who can access what, or debugging 403/404 responses. Covers all 5 rule types, filter syntax, operators, request/collection macros, and field modifiers.
API rules and filter expressions for PocketBase access control. Use when setting permissions, writing filter expressions, configuring who can access what, or debugging 403/404 responses. Covers all 5 rule types, filter syntax, operators, request/collection macros, and field modifiers.
PocketBase API Rules & Filter Expressions
Rule Types
Each collection has 5 rule types. Each rule is a filter expression that must evaluate to true for the request to proceed.
Rule
Controls
Locked =
Empty string =
List
GET /api/collections/{name}/records
superusers only
everyone can list
View
GET /api/collections/{name}/records/{id}
superusers only
everyone can view
Create
POST /api/collections/{name}/records
superusers only
everyone can create
Update
PATCH /api/collections/{name}/records/{id}
superusers only
everyone can update
Delete
DELETE /api/collections/{name}/records/{id}
superusers only
everyone can delete
Critical: null/locked means only superusers can perform the action (regular users and guests are denied). Empty string "" means EVERYONE including guests. Superusers always bypass API rules entirely — see below.
Superuser Bypass
Superusers (formerly admins) always bypass API rules. Rules only apply to regular auth records and guests.
Filter Syntax
Operators
Operator
Meaning
Example
=
Equal
status = "active"
!=
Not equal
status != "draft"
>
Greater than
count > 5
>=
Greater or equal
count >= 5
<
Less than
count < 10
<=
Less or equal
count <= 10
~
LIKE (contains)
title ~ "hello"
!~
NOT LIKE
title !~ "spam"
?=
Any/has (array contains)
tags ?= "TAG_ID"
?!=
None (array not contains)
tags ?!= "TAG_ID"
?>
Any greater than
scores ?> 90
?>=
Any greater or equal
scores ?>= 90
?<
Any less than
scores ?< 10
?<=
Any less or equal
scores ?<= 10
?~
Any LIKE
emails ?~ "@gmail.com"
?!~
Any NOT LIKE
emails ?!~ "@test.com"
Critical: use ?= (not =) for multi-valued fields (multi-select, multi-relation, multi-file). = checks the raw JSON string, ?= checks individual values.
Logical Operators
status = "active" && author = @request.auth.id
status = "active" || status = "featured"
Parentheses for grouping: (a = 1 || b = 2) && c = 3
Values
Strings: "value" or 'value'
Numbers: 123, 45.67
Booleans: true, false
null — empty/missing value
Identifiers: field names, macros
Request Macros (@request.*)
Access the current request context in rules:
Macro
Type
Description
@request.auth.id
string
Current auth record ID (empty if guest)
@request.auth.email
string
Current auth record email
@request.auth.verified
bool
Whether email is verified
@request.auth.collectionId
string
Auth collection ID
@request.auth.collectionName
string
Auth collection name
@request.auth.*
any
Any field from the auth record
@request.body.fieldName
any
Field value from request body
@request.query.paramName
string
URL query parameter
@request.headers.name
string
Request header (lowercase key)
@request.method
string
HTTP method (GET/POST/PATCH/DELETE)
Auth record relations
You can traverse relations on the auth record:
@request.auth.team.owner = @request.auth.id
Collection Macros (@collection.*)
Cross-collection lookups without explicit joins:
@collection.memberships.user ?= @request.auth.id &&
@collection.memberships.team ?= team
This checks if a record exists in the memberships collection where the user matches the current auth user and the team matches the current record's team field.
Note: @collection.* performs an implicit EXISTS subquery. It's powerful but can be slow on large datasets — add indexes.
Field Modifiers
Use in create/update rules to validate specific field behaviors:
Modifier
Works on
Description
:isset
@request.body.*
True if the field was sent in the request (even if empty)
:changed
record field
True if the field value differs from current stored value (update only)
:length
string/array
Returns the length
:each
array
Applies the condition to each element
:lower
string
Lowercased value
Examples
// Only allow changing status if user is owner
status:changed = false || author = @request.auth.id
// Prevent setting role on create
@request.body.role:isset = false
// Require at least 2 tags
@request.body.tags:length >= 2
// Check each tag is from allowed list
@request.body.tags:each ?= @collection.allowed_tags.id