Basics
Mongalayer's access control system lets you define roles with filters, document-level permissions, field-level permissions, delete permissions, and validators. Each request is evaluated against these definitions to determine what data the caller can access.
Access definitions
An access configuration is an array of AccessDefinition objects, one per role:
import { AccessPermissions, type AccessConfig, type AccessDefinition } from "@mongalayer/server";
// As a single definition object:
const memberProjectAccess: AccessDefinition<Project> = {
role: "member",
filter: {
members: { "$in": ["%%user.id"] }
},
document: AccessPermissions.Read
};
// Directly in the access array:
export const projectAccess: AccessConfig<Project> = [{
role: "owner",
filter: {
owner: "%%user.id"
},
document: AccessPermissions.ReadWrite,
delete: true
}, {
role: "contributor",
filter: {
editors: { "$in": ["%%user.id"] }
},
document: AccessPermissions.Read,
fields: {
title: AccessPermissions.ReadUpdate,
description: AccessPermissions.ReadUpdate
},
},
// Add the member role from before
memberProjectAccess
];
Properties
| Property | Type | Description |
|---|---|---|
role | string | A unique name identifying this role |
filter | AccessFilter | A filter that determines which documents this role applies to |
document | AccessPermission | The default permission level for the entire document |
fields | Record<string, AccessPermission> | Per-field permission overrides, only root level fields |
delete | boolean | Whether this role allows deleting documents |
collection | AccessAlternativeCollection | Use an alternative collection for role resolution |
validators | object | Custom validation functions for create and update operations |
Access permissions
Permissions are defined using bitwise flags:
import { AccessPermissions } from "@mongalayer/server";
AccessPermissions.None // 0 — no access
AccessPermissions.Read // 0b0001 — read only
AccessPermissions.Create // 0b0010 — create only
AccessPermissions.Update // 0b0100 — update only
AccessPermissions.ReadUpdate // 0b0101 — read + update (no create)
AccessPermissions.ReadWrite // 0b0111 — read + create + update
You can combine permissions using bitwise operators:
// Read + Create, but no Update
const permission = AccessPermissions.Read | AccessPermissions.Create;
// ReadWrite without Update
const permission = AccessPermissions.ReadWrite & ~AccessPermissions.Update;
// or equivalently:
const permission = AccessPermissions.ReadWrite ^ AccessPermissions.Update;
Access filters
Filters determine which documents a role applies to. They use a subset of MongoDB's query syntax with support for hydration — injecting values from the access payload at runtime.
Hydration with %%
Any string value prefixed with %% is replaced with the corresponding value from the access payload:
// Access payload: { user: { id: "user-123", org: "org-456" } }
{
role: "self",
filter: {
_id: "%%user.id" // becomes: { _id: "user-123" }
}
}
{
role: "org-member",
filter: {
organizationId: "%%user.org" // becomes: { organizationId: "org-456" }
}
}
Supported filter operators
Property-level operators:
| Operator | Description |
|---|---|
$eq | Equals |
$ne | Not equals |
$in | In array |
$nin | Not in array |
$exists | Field exists |
Root-level operators:
| Operator | Description |
|---|---|
$and | All conditions must match |
$or | At least one condition must match |
$nor | None of the conditions must match |
Custom operators (access filters only):
| Operator | Description |
|---|---|
$$eq | Compare two values for equality |
$$in | Check if a value is in an array |
$$ne | Compare two values for inequality |
$$nin | Check if a value is not in an array |
Examples
// Simple equality
{ owner: "%%user.id" }
// Array membership
{ members: { "$in": ["%%user.id"] } }
// Multiple conditions - Owner or contributor
{
"$or": [
{ "owner": { "$eq": "%%user.id" } },
{ "contributors": { "$in": ["%%user.id"] } }
]
}
// Custom equality operator - match specific user
{
"$$eq": ["%%user.id", "ABC"]
}
// Custom includes operator - check if user has admin role
{
"$$in": [ "admin", "%%user.roles" ]
}
// Documents filter - exclude archived documents
{
"$or": [
{ "archived": { "$exists": false } },
{ "archived": { "$ne": true } }
]
}
Field-level permissions
The fields property lets you override the document-level permission for specific top-level fields:
{
role: "member",
document: AccessPermissions.Read, // Default: read-only
fields: {
config: AccessPermissions.None, // config field: completely hidden
name: AccessPermissions.ReadWrite, // name field: read + write
description: AccessPermissions.ReadUpdate // description: read + update (no create)
createdAt: AccessPermissions.Read | AccessPermissions.Create, // createdAt: read + create
}
}
When a field is not listed in fields, it inherits the document permission. When document is not set on the role, it falls back to accessDefaults.document option of the Mongalayer instance configuration as shown below.
Default permissions
When creating a Mongalayer instance, you can set default permissions that apply when no role matches or when a role doesn't explicitly set a permission:
const layer = new Mongalayer(mongoClient, collections, {
accessDefaults: {
document: AccessPermissions.Read, // Default: read-only
delete: false // Default: no deletion
}
});
Role resolution
When a query is executed, Mongalayer determines which role applies to each document by evaluating the access filters. A __mongalayer_role field is temporarily added to each document during processing to track the matched role.
- If no roles are defined (empty access array), all documents are accessible with default permissions
- If roles are defined but no role matches a document, the document is inaccessible
- If multiple roles could match, the first matching role in the array is used
The order of roles in your access configuration matters — put the role with the least permissions last. For example: admin -> owner -> contributor -> member