skillZsskillZsskillZs
HAND-TAGGED >>> 991 SKILLS LIVE <<<* OPEN SOURCE *NO LOGIN, NO TRACKING FRESH DROPS WEEKLY HAND-TAGGED >>> 991 SKILLS LIVE <<<* OPEN SOURCE *NO LOGIN, NO TRACKING FRESH DROPS WEEKLY HAND-TAGGED >>> 991 SKILLS LIVE <<<* OPEN SOURCE *NO LOGIN, NO TRACKING FRESH DROPS WEEKLY HAND-TAGGED >>> 991 SKILLS LIVE <<<* OPEN SOURCE *NO LOGIN, NO TRACKING FRESH DROPS WEEKLY HAND-TAGGED >>> 991 SKILLS LIVE <<<* OPEN SOURCE *NO LOGIN, NO TRACKING FRESH DROPS WEEKLY HAND-TAGGED >>> 991 SKILLS LIVE <<<* OPEN SOURCE *NO LOGIN, NO TRACKING FRESH DROPS WEEKLY
← back to zine
PocketBase API RulesSKILL #ULES
Coding

PocketBase API Rules

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.

↗ github · ★ 27k·src: davila7/claude-code-templates

the manual

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.

RuleControlsLocked =Empty string =
ListGET /api/collections/{name}/recordssuperusers onlyeveryone can list
ViewGET /api/collections/{name}/records/{id}superusers onlyeveryone can view
CreatePOST /api/collections/{name}/recordssuperusers onlyeveryone can create
UpdatePATCH /api/collections/{name}/records/{id}superusers onlyeveryone can update
DeleteDELETE /api/collections/{name}/records/{id}superusers onlyeveryone 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

OperatorMeaningExample
=Equalstatus = "active"
!=Not equalstatus != "draft"
>Greater thancount > 5
>=Greater or equalcount >= 5
<Less thancount < 10
<=Less or equalcount <= 10
~LIKE (contains)title ~ "hello"
!~NOT LIKEtitle !~ "spam"
?=Any/has (array contains)tags ?= "TAG_ID"
?!=None (array not contains)tags ?!= "TAG_ID"
?>Any greater thanscores ?> 90
?>=Any greater or equalscores ?>= 90
?<Any less thanscores ?< 10
?<=Any less or equalscores ?<= 10
?~Any LIKEemails ?~ "@gmail.com"
?!~Any NOT LIKEemails ?!~ "@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:

MacroTypeDescription
@request.auth.idstringCurrent auth record ID (empty if guest)
@request.auth.emailstringCurrent auth record email
@request.auth.verifiedboolWhether email is verified
@request.auth.collectionIdstringAuth collection ID
@request.auth.collectionNamestringAuth collection name
@request.auth.*anyAny field from the auth record
@request.body.fieldNameanyField value from request body
@request.query.paramNamestringURL query parameter
@request.headers.namestringRequest header (lowercase key)
@request.methodstringHTTP 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:

ModifierWorks onDescription
:isset@request.body.*True if the field was sent in the request (even if empty)
:changedrecord fieldTrue if the field value differs from current stored value (update only)
:lengthstring/arrayReturns the length
:eacharrayApplies the condition to each element
:lowerstringLowercased 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

Datetime Macros

MacroExample output
@now2024-01-15 10:30:00.000Z
@second2024-01-15 10:30:00.000Z
@minute2024-01-15 10:30:00.000Z
@hour2024-01-15 10:00:00.000Z
@day2024-01-15 00:00:00.000Z
@month2024-01-01 00:00:00.000Z
@year2024-01-01 00:00:00.000Z
@todayStart2024-01-15 00:00:00.000Z
@todayEnd2024-01-15 23:59:59.999Z
@monthStart2024-01-01 00:00:00.000Z
@monthEnd2024-01-31 23:59:59.999Z
@yearStart2024-01-01 00:00:00.000Z
@yearEnd2024-12-31 23:59:59.999Z

Arithmetic: @now - 7d, @now + 1h, @now - 30m

geoDistance()

For location-based filtering:

geoDistance(lat, lon, 40.7128, -74.0060) <= 10000

Arguments: geoDistance(latField, lonField, targetLat, targetLon) — returns meters.

Common Patterns

Owner-only access

// View/Update/Delete rule:
author = @request.auth.id

Authenticated users only

@request.auth.id != ""

Verified users only

@request.auth.verified = true

Role-based access

@request.auth.role = "admin" || author = @request.auth.id

Team membership

@collection.team_members.user ?= @request.auth.id &&
@collection.team_members.team ?= team

Public read, owner write

// List/View: ""  (empty = everyone)
// Create: @request.auth.id != ""
// Update/Delete: author = @request.auth.id

Prevent field modification

// Update rule: prevent changing `owner` after creation
owner:changed = false

Time-limited access

expires > @now

more coding

Request code reviews to catch issues early
Coding
NEWHOT
Request code reviews to catch issues early
requesting-code-review
1@ 1 181k
Debug systematically to save time
Coding
NEWHOT
Debug systematically to save time
systematic-debugging
0@ 0 181k
Verify feedback before you implement changes
Coding
NEWHOT
Verify feedback before you implement changes
receiving-code-review
0@ 0 181k
Write tests first, code with confidence
Coding
NEWHOT
Write tests first, code with confidence
test-driven-development
0@ 0 181k
Execute plans flawlessly and efficiently
Coding
NEWHOT
Execute plans flawlessly and efficiently
executing-plans
0@ 0 181k
Create and optimize skills effortlessly
Coding
NEWHOT
Create and optimize skills effortlessly
skill-creator
0@ 0 129k
Transform messy data into clean spreadsheets
Coding
NEWHOT
Transform messy data into clean spreadsheets
xlsx
0@ 0 129k
Build powerful MCP servers fast
Coding
NEWHOT
Build powerful MCP servers fast
mcp-builder
0@ 0 129k