Skip to content

Permissions

Permissions define what each role can do with a resource. Every permission ties a role to an action (create, read, update, or delete) and specifies up to three layers of access control: fields, filters, and checks.

The Three-Layer Model

Fields

The fields array lists the field names a role is allowed to interact with for the given action. Only the specified fields are accepted in request bodies (for writes) or returned in responses (for reads).

{
  "role": "viewer",
  "action": "read",
  "fields": ["id", "title", "body", "created_at"]
}

System fields (id, created_at, updated_at) are always accessible and do not need to be explicitly listed.

Filters

The filters array applies WHERE-clause constraints that limit which records a role can access. Filters are evaluated at query time and are primarily used for read operations.

{
  "role": "user",
  "action": "read",
  "filters": [
    { "field": "owner_id", "operator": "eq", "claim": "id" },
    { "field": "status", "operator": "neq", "value": "archived" }
  ]
}

In this example, the user can only read records they own that are not archived.

Checks

The checks array applies validation constraints evaluated at write time. They are used for create, update, and delete actions to enforce business rules and enable automatic value injection.

{
  "role": "user",
  "action": "create",
  "fields": ["title", "body"],
  "checks": [
    { "field": "owner_id", "operator": "eq", "claim": "id" }
  ]
}

When a check uses the eq operator with a claim or value, the target field becomes optional in the request body and its value is automatically injected. See Claims-Based Auto-Injection below.

Constraint Operators

Constraints used in filters and checks support 27 operators grouped by type:

Comparison

Operator Description
eq Equal
neq Not equal
gt Greater than
lt Less than
gte Greater than or equal to
lte Less than or equal to

Membership

Operator Description
in Value in array
nin Value not in array
is_null Field is null
is_not_null Field is not null

String

Operator Description
like SQL LIKE pattern matching
ilike Case-insensitive LIKE
similar SQL SIMILAR TO pattern
regex Regular expression match
iregex Case-insensitive regular expression

JSON

Operator Description
contains JSON contains key/value
contained_in Value is contained in JSON
has_key JSON object has specific key
has_keys_any JSON has any of the given keys
has_keys_all JSON has all of the given keys

Relationship

Operator Description
exists Related record exists
not_exists Related record does not exist

Logical

Operator Description
_and Logical AND
_or Logical OR
_not Logical NOT

Each constraint object has the following structure:

{
  "field": "owner_id",
  "operator": "eq",
  "value": "some-static-value",
  "claim": "id"
}

Use value for static comparisons and claim for dynamic values sourced from the authenticated user.

Claims-Based Auto-Injection

When a check constraint uses the eq operator with either a claim or a value, Snaapi automatically handles the target field:

  1. The field becomes optional in the request schema — callers do not need to provide it.
  2. At request time, the value is injected automatically from the claim source or static value.
  3. If the caller provides a value for the field, it is overridden by the injected value. This prevents tampering — users cannot set their own owner_id, for example.

Claim Sources

Claims reference fields from the authenticated user object using dot notation:

Claim Example Resolves To
id The user's ID
email The user's email address
metadata.team_id A nested value in user metadata

Example

Given this permission:

{
  "role": "user",
  "action": "create",
  "fields": ["title", "body"],
  "checks": [
    { "field": "owner_id", "operator": "eq", "claim": "id" }
  ]
}

A user creating a post only needs to provide title and body. The owner_id field is automatically set to the authenticated user's ID, and any user-supplied owner_id value is discarded.

Admin Token Restrictions

When an admin authenticates via an API key (admin token), resource endpoints restrict write operations:

Operation Allowed
GET (read) yes
DELETE yes
POST (create) no
PUT/PATCH (update) no

Create and update requests return a 403 response with error code ADMIN_TOKEN_NOT_ALLOWED. Use the dedicated admin endpoints for these operations instead.

This restriction ensures that write operations by admin tokens go through dedicated admin endpoints with proper audit trails.