Convex adapter :: Cerbos Authorization Management Platform // Documentation
Convex adapter
| This documentation is for an as-yet unreleased version of Cerbos PDP. Choose 0.53.0 from the version picker at the top right or navigate to https://docs.cerbos.dev for the latest version. |
The @cerbos/orm-convex package converts a Cerbos PlanResources response into a Convex filter function. Authorization conditions are split between a database-level filter and an optional JavaScript post-filter for operators that Convex cannot express natively.
Requirements
Cerbos >= v0.16
@cerbos/httpor@cerbos/grpcclientConvex 1.x
Node.js >= 20.0.0
Installation
npm install @cerbos/orm-convex
bashCopied!
Supported operators
Database-level operators
| Category | Operators |
|---|---|
| Logical | and, or, not — q.and(…), q.or(…), q.not(…) |
| Comparison | eq, ne, lt, le, gt, ge — q.eq, q.neq, q.lt, q.lte, q.gt, q.gte |
| Membership | in — composed as q.or(q.eq(field, v1), q.eq(field, v2), …) |
| Existence | isSet — q.neq(field, undefined) or q.eq(field, undefined) |
Post-filter operators
The following operators cannot be expressed as Convex database filters. When encountered, the adapter returns a postFilter function that evaluates them in JavaScript:
| Category | Operators |
|---|---|
| String | contains, startsWith, endsWith |
| Collection | hasIntersection, exists, exists_one, all, filter, map, lambda |
For and(…) expressions with mixed operator types, the adapter splits the tree: database-pushable children go to filter, the rest go to postFilter. For or(…) with any unsupported child, the entire expression goes to postFilter to avoid missing results.
allowPostFilter opt-in
By default, queryPlanToConvex throws when the query plan requires a postFilter. This is because post-filter operators cause data to be fetched before authorization filtering is fully applied. To opt in:
const { kind, filter, postFilter } = queryPlanToConvex({
queryPlan,
mapper,
allowPostFilter: true,
});
typescriptCopied!
If your policies only use operators that Convex supports natively, filter alone enforces the full policy at the database level and this flag is not needed.
Usage
import { queryPlanToConvex, PlanKind } from "@cerbos/orm-convex";
const queryPlan = await cerbos.planResources({
principal: { id: "user1", roles: ["USER"] },
resource: { kind: "document" },
action: "view",
});
const { kind, filter, postFilter } = queryPlanToConvex({
queryPlan,
mapper,
allowPostFilter: true,
});
if (kind === PlanKind.ALWAYS_DENIED) return [];
if (kind === PlanKind.ALWAYS_ALLOWED && !postFilter) {
return await ctx.db.query("documents").collect();
}
let query = ctx.db.query("documents");
if (filter) query = query.filter(filter);
let results = await query.collect();
if (postFilter) results = results.filter(postFilter);
return results;
typescriptCopied!
Field mapper
const mapper = {
"request.resource.attr.title": { field: "title" },
"request.resource.attr.status": { field: "status" },
};
// Or as a function:
const mapper = (path) => ({
field: path.replace("request.resource.attr.", ""),
});
typescriptCopied!
The field property rewrites a Cerbos path to a Convex document field. Dot notation is supported for nested fields. If the mapper is omitted, the adapter uses query plan paths as-is.
Limitations
String and collection operators are evaluated as a JavaScript
postFilterafter the database query returns. These conditions do not reduce the number of documents read from the database.For
or(…)expressions where any child uses an unsupported operator, the entire OR is evaluated viapostFilter.The
inoperator is composed as multipleeqcomparisons joined withor, which may be less efficient for large value lists.