Appearance
Access - Role & Condition-Based Access Control
Define who can perform CRUD operations and under what conditions.
Configuration Options
typescript
interface Access {
// Required roles (OR logic - user needs at least one)
roles?: string[];
// Record-level conditions
record?: {
[field: string]: FieldCondition;
};
// Combinators
or?: Access[];
and?: Access[];
}
// Field conditions
type FieldCondition =
| { equals: value | '$ctx.userId' | '$ctx.activeOrgId' }
| { notEquals: value }
| { in: value[] }
| { notIn: value[] }
| { lessThan: number }
| { greaterThan: number }
| { lessThanOrEqual: number }
| { greaterThanOrEqual: number };CRUD Configuration
typescript
crud: {
// LIST - GET /resource
list: {
access: { roles: ["member", "admin"] },
pageSize: 25, // Default page size
maxPageSize: 100, // Client can't exceed this
fields: ['id', 'name', 'status'], // Selective field returns (optional)
},
// GET - GET /resource/:id
get: {
access: { roles: ["member", "admin"] },
fields: ['id', 'name', 'status', 'details'], // Optional field selection
},
// CREATE - POST /resource
create: {
access: { roles: ["member", "admin"] },
defaults: { // Default values for new records
status: 'pending',
isActive: true,
},
},
// UPDATE - PATCH /resource/:id
update: {
access: {
or: [
{ roles: ["admin"] },
{ roles: ["member"], record: { status: { equals: "draft" } } }
]
},
},
// DELETE - DELETE /resource/:id
delete: {
access: { roles: ["admin"] },
mode: "soft", // 'soft' (default) or 'hard'
},
// PUT - PUT /resource/:id (only when generateId: false + guards: false)
put: {
access: { roles: ["admin", "sync-service"] },
},
}List Filtering (Query Parameters)
The LIST endpoint automatically supports filtering via query params:
GET /rooms?status=active # Exact match
GET /rooms?capacity.gt=10 # Greater than
GET /rooms?capacity.gte=10 # Greater than or equal
GET /rooms?capacity.lt=50 # Less than
GET /rooms?capacity.lte=50 # Less than or equal
GET /rooms?status.ne=deleted # Not equal
GET /rooms?name.like=Conference # Pattern match (LIKE %value%)
GET /rooms?status.in=active,pending,review # IN clause| Operator | Query Param | SQL Equivalent |
|---|---|---|
| Equals | ?field=value | WHERE field = value |
| Not equals | ?field.ne=value | WHERE field != value |
| Greater than | ?field.gt=value | WHERE field > value |
| Greater or equal | ?field.gte=value | WHERE field >= value |
| Less than | ?field.lt=value | WHERE field < value |
| Less or equal | ?field.lte=value | WHERE field <= value |
| Pattern match | ?field.like=value | WHERE field LIKE '%value%' |
| In list | ?field.in=a,b,c | WHERE field IN ('a','b','c') |
Sorting & Pagination
GET /rooms?sort=createdAt&order=desc # Sort by field
GET /rooms?limit=25&offset=50 # Pagination- Default limit: 50
- Max limit: 100 (or
maxPageSizeif configured) - Default order:
asc
Delete Modes
typescript
delete: {
access: { roles: ["admin"] },
mode: "soft", // Sets deletedAt/deletedBy, record stays in DB
}
delete: {
access: { roles: ["admin"] },
mode: "hard", // Permanent deletion from database
}Context Variables
Use $ctx. prefix to reference context values in conditions:
typescript
// User can only view their own records
access: {
record: { userId: { equals: "$ctx.userId" } }
}
// Available context variables:
// $ctx.userId - Current user's ID
// $ctx.activeOrgId - Current organization ID
// $ctx.activeTeamId - Current team ID
// $ctx.{property} - Any custom context property