API Rules Documentation - Java SDK
API Rules are collection access controls and data filters that determine who can perform actions on your collections and what data they can access.
Overview
Each collection has 5 standard API rules, corresponding to specific API actions:
listRule- Controls read/list accessviewRule- Controls read/view accesscreateRule- Controls create accessupdateRule- Controls update accessdeleteRule- Controls delete access
Auth collections have two additional rules:
manageRule- Admin-like permissions for managing auth recordsauthRule- Additional constraints applied during authentication
Rule Values
Each rule can be set to one of three values:
1. null (Locked)
Only authorized superusers can perform the action.
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", null);
pb.collections.update("products", updateData, null, null);
2. "" (Empty String - Public)
Anyone (superusers, authorized users, and guests) can perform the action.
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "");
pb.collections.update("products", updateData, null, null);
3. Non-empty String (Filter Expression)
Only users satisfying the filter expression can perform the action.
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "@request.auth.id != \"\"");
pb.collections.update("products", updateData, null, null);
Setting Rules
Individual Rules
Set individual rules by updating the collection:
// Set list rule
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "@request.auth.id != \"\"");
pb.collections.update("products", updateData, null, null);
// Set view rule
Map<String, Object> viewRuleData = new HashMap<>();
viewRuleData.put("viewRule", "@request.auth.id != \"\"");
pb.collections.update("products", viewRuleData, null, null);
// Set create rule
Map<String, Object> createRuleData = new HashMap<>();
createRuleData.put("createRule", "@request.auth.id != \"\"");
pb.collections.update("products", createRuleData, null, null);
// Set update rule
Map<String, Object> updateRuleData = new HashMap<>();
updateRuleData.put("updateRule", "@request.auth.id != \"\" && author.id ?= @request.auth.id");
pb.collections.update("products", updateRuleData, null, null);
// Set delete rule
Map<String, Object> deleteRuleData = new HashMap<>();
deleteRuleData.put("deleteRule", null); // Only superusers
pb.collections.update("products", deleteRuleData, null, null);
Bulk Rule Updates
Set multiple rules at once:
Map<String, Object> rules = new HashMap<>();
rules.put("listRule", "@request.auth.id != \"\"");
rules.put("viewRule", "@request.auth.id != \"\"");
rules.put("createRule", "@request.auth.id != \"\"");
rules.put("updateRule", "@request.auth.id != \"\" && author.id ?= @request.auth.id");
rules.put("deleteRule", null); // Only superusers
pb.collections.update("products", rules, null, null);
Getting Rules
Retrieve all rules for a collection:
ObjectNode collection = pb.collections.getOne("products", null, null, null, null);
System.out.println(collection.path("listRule").asText());
System.out.println(collection.path("viewRule").asText());
Filter Syntax
Rules use the same filter syntax as API queries. The syntax follows: OPERAND OPERATOR OPERAND
Operators
=- Equal!=- NOT equal>- Greater than>=- Greater than or equal<- Less than<=- Less than or equal~- Like/Contains (auto-wraps string in%for wildcard)!~- NOT Like/Contains?=- Any/At least one of Equal?!=- Any/At least one of NOT equal?>- Any/At least one of Greater than?>=- Any/At least one of Greater than or equal?<- Any/At least one of Less than?<=- Any/At least one of Less than or equal?~- Any/At least one of Like/Contains?!~- Any/At least one of NOT Like/Contains
Logical Operators
&&- AND||- OR(...)- Grouping parentheses
Field Access
Collection Schema Fields
Access fields from your collection schema:
// Filter by status field
String filter = "status = \"active\"";
// Access nested relation fields
String filter = "author.status != \"banned\"";
// Access relation IDs
String filter = "author.id ?= @request.auth.id";
Request Context (@request.*)
Access current request data:
// Authentication state
String rule = "@request.auth.id != \"\""; // User is authenticated
String rule = "@request.auth.id = \"\""; // User is guest
// Request context
String rule = "@request.context != \"oauth2\""; // Not an OAuth2 request
// HTTP method
String rule = "@request.method = \"GET\"";
// Request headers (normalized: lowercase, "-" replaced with "_")
String rule = "@request.headers.x_token = \"test\"";
// Query parameters
String rule = "@request.query.page = \"1\"";
// Body parameters
String rule = "@request.body.title != \"\"";
Other Collections (@collection.*)
Target other collections that share common field values:
// Check if user has access in related collection
String rule = "@collection.permissions.user ?= @request.auth.id && @collection.permissions.resource = id";
Field Modifiers
:isset Modifier
Check if a request field was submitted:
// Prevent changing role field
String rule = "@request.body.role:isset = false";
:length Modifier
Check the number of items in an array field:
// At least 2 items in select field
String rule = "@request.body.tags:length > 1";
// Check existing relation field length
String rule = "someRelationField:length = 2";
:each Modifier
Apply condition to each item in a multiple field:
// All select options contain "create"
String rule = "@request.body.someSelectField:each ~ \"create\"";
// All fields have "pb_" prefix
String rule = "someSelectField:each ~ \"pb_%\"";
:lower Modifier
Perform case-insensitive string comparisons:
// Case-insensitive title check
String rule = "@request.body.title:lower = \"test\"";
// Case-insensitive existing field match
String rule = "title:lower ~ \"test\"";
DateTime Macros
All macros are UTC-based:
// Current datetime
String macro = "@now";
// Date components
String macro = "@second"; // 0-59
String macro = "@minute"; // 0-59
String macro = "@hour"; // 0-23
String macro = "@weekday"; // 0-6
String macro = "@day"; // Day number
String macro = "@month"; // Month number
String macro = "@year"; // Year number
// Relative dates
String macro = "@yesterday";
String macro = "@tomorrow";
String macro = "@todayStart"; // Beginning of current day
String macro = "@todayEnd"; // End of current day
String macro = "@monthStart"; // Beginning of current month
String macro = "@monthEnd"; // End of current month
String macro = "@yearStart"; // Beginning of current year
String macro = "@yearEnd"; // End of current year
Example:
String rule = "@request.body.publicDate >= @now";
String rule = "created >= @todayStart && created <= @todayEnd";
Functions
geoDistance(lonA, latA, lonB, latB)
Calculate Haversine distance between two geographic points in kilometres:
// Offices within 25km
String rule = "geoDistance(address.lon, address.lat, 23.32, 42.69) < 25";
Common Examples
Allow Only Registered Users
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "@request.auth.id != \"\"");
pb.collections.update("products", updateData, null, null);
Filter by Status
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "status = \"active\"");
pb.collections.update("products", updateData, null, null);
Combine Conditions
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "@request.auth.id != \"\" && (status = \"active\" || status = \"pending\")");
pb.collections.update("products", updateData, null, null);
Filter by Relation
// Only show records where user is the author
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "@request.auth.id != \"\" && author.id ?= @request.auth.id");
pb.collections.update("posts", updateData, null, null);
// Only show records where user is in allowed_users relation
Map<String, Object> updateData2 = new HashMap<>();
updateData2.put("listRule", "@request.auth.id != \"\" && allowed_users.id ?= @request.auth.id");
pb.collections.update("documents", updateData2, null, null);
Public Access with Filter
// Allow anyone, but only show active items
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "status = \"active\"");
pb.collections.update("products", updateData, null, null);
// Allow anyone, filter by title prefix
Map<String, Object> updateData2 = new HashMap<>();
updateData2.put("listRule", "title ~ \"Lorem%\"");
pb.collections.update("articles", updateData2, null, null);
Owner-Based Update/Delete
// Users can only update/delete their own records
Map<String, Object> updateData = new HashMap<>();
updateData.put("updateRule", "@request.auth.id != \"\" && author.id = @request.auth.id");
pb.collections.update("posts", updateData, null, null);
Map<String, Object> deleteData = new HashMap<>();
deleteData.put("deleteRule", "@request.auth.id != \"\" && author.id = @request.auth.id");
pb.collections.update("posts", deleteData, null, null);
Prevent Field Modification
// Prevent changing role field
Map<String, Object> updateData = new HashMap<>();
updateData.put("updateRule", "@request.auth.id != \"\" && @request.body.role:isset = false");
pb.collections.update("users", updateData, null, null);
Date-Based Rules
// Only show future events
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "startDate >= @now");
pb.collections.update("events", updateData, null, null);
// Only show items created today
Map<String, Object> updateData2 = new HashMap<>();
updateData2.put("listRule", "created >= @todayStart && created <= @todayEnd");
pb.collections.update("posts", updateData2, null, null);
Array Field Validation
// Require at least one tag
Map<String, Object> updateData = new HashMap<>();
updateData.put("createRule", "@request.body.tags:length > 0");
pb.collections.update("posts", updateData, null, null);
// Require all tags to start with "pb_"
Map<String, Object> updateData2 = new HashMap<>();
updateData2.put("createRule", "@request.body.tags:each ~ \"pb_%\"");
pb.collections.update("posts", updateData2, null, null);
Geographic Distance
// Only show offices within 25km of location
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "geoDistance(address.lon, address.lat, 23.32, 42.69) < 25");
pb.collections.update("offices", updateData, null, null);
Best Practices
- Start with locked rules (null) for security, then gradually open access as needed
- Use relation checks for owner-based access patterns
- Combine multiple conditions using
&&and||for complex scenarios - Test rules thoroughly before deploying to production
- Document your rules in code comments explaining the business logic
- Use empty string (
\"\") only when you truly want public access - Leverage modifiers (
:isset,:length,:each) for validation
Error Responses
API Rules also act as data filters. When a request doesn’t satisfy a rule:
- listRule - Returns
200with empty items (filters out records) - createRule - Returns
400Bad Request - viewRule - Returns
404Not Found - updateRule - Returns
404Not Found - deleteRule - Returns
404Not Found - All rules - Return
403Forbidden if locked (null) and user is not superuser
Notes
- Superusers bypass all rules - Rules are ignored when the action is performed by an authorized superuser
- Rules are evaluated server-side - Client-side validation is not enough
- Comments are supported - Use
//for single-line comments in rules - System fields protection - Some fields may be protected regardless of rules