Vext Plugin API
Purpose and preconditions
permission-core/plugins/vext is the optional integration entry for Vext 0.3.26. It initializes PermissionCore in the Vext lifecycle, installs route guards, maps domain errors to HTTP responses, enhances Vext-native app.db.collection() / app.db.model() inside protected requests, and exposes request-scoped permission APIs plus response-field projection for protected routes.
Before using it:
- Node.js is
>=20.19.0. - The host provides a connected MonSQLize 3.1 instance.
- The authentication plugin runs first and writes trusted identity to
req.auth. - Response-field projection requires the matching
api:resource and fields to be saved throughmenus.responses.set()ormenus.config.save().
Signatures
routes.protect applies the same default guard as permission: true: invoke + api:METHOD:/path, for example api:GET:/orders/:id. Route-level permission still exists for explicit overrides or combined requirements. Protected routes with caching enabled fail closed unless route caching is explicitly disabled, preventing user-specific projections from becoming shared cached responses.
Parameters
PermissionVextPluginOptions
RouteOptions.permission
Method details
permissionPlugin(options?)
- Purpose: Create the permission plugin descriptor registered with Vext.
- Parameters:
optionsare listed above; at most one database source may be provided, and authentication must run first. - State impact: During Vext setup it creates and initializes core, installs request middleware, route hooks, error mapping, and exposes
app.permission; during Vext close it closes PermissionCore. - Raw return: Synchronously returns
VextPlugin, not a setup result and not aPermissionCoreinstance.
hasPermissionContext(req)
- Purpose: Check whether the current request already has a permission context and narrow the TypeScript type.
- Parameters: Current Vext
req. - State impact: Checks the internal owner marker only; it does not lazily resolve the subject.
- Raw return:
boolean;truenarrows req toPermissionVextRequest.
requirePermissionContext(req)
- Purpose: Get the permission API for the current request.
- Parameters: Current Vext request that passed through the permission plugin middleware.
- State impact: Lazily resolves and freezes the subject for this request only; it does not write authorization data.
- Raw return:
Promise<VextRequestPermissionApi>withsubject,can,assert, optionaldata, andfilterResponse.
req.auth.permission.data.collection(name)
- Purpose: Create a guarded collection facade inside the current Vext request for authorized reads, writes, counts, or pagination.
- Parameters:
nameis the host MonSQLize collection name; resource and scope fields come fromdata.collections[name]or the defaultdb:${name}plusdata.scopeFields. - State impact: Creating the facade does not access the database; every
find/insert/update/deletecall re-checks the current request owner, subject, scope, row rules, and field permissions. - Raw return:
AuthorizedCollection<TDocument, TCreate>; it is not a full MonSQLize collection and does not exposeraw().
When data.exposeAs: 'monsqlize' is configured, req.monsqlize.collection(name) is an optional alias for the same request data facade. Do not cache it across requests.
When data.exposeAs: 'db' is configured, req.db.collection(name) is an optional alias for the same request data facade.
req.monsqlize and req.db are optional in the public type because aliases only exist when configured. In TypeScript handlers, call requirePermissionContext(req) when you want a narrowed permission object, then read permission.data from the returned value.
req.auth.permission.data.model(name)
- Purpose: Create a guarded Vext/MonSQLize model facade inside the current request for authorized basic CRUD.
- Parameters:
nameis resolved through the host MonSQLizemodel(name)API. The model'scollectionNameselects the physical collection;data.collections[collectionName]may override the logicaldb:*resource. - State impact: Creating the facade does not access the database; each supported CRUD call delegates to the authorized collection facade for the current request.
- Raw return:
VextAuthorizedModel<TDocument, TCreate>with basicfind/findOne/count/findPage/insertOne/updateOne/updateMany/deleteOne/deleteManysupport. Advanced methods such asraw(),aggregate(),watch(), and index management throwDATA_OPERATION_UNSUPPORTEDinside the protected facade.
When data.transparent: true is configured, protected requests can usually keep using app.db.model(name) instead of calling this explicit API.
Transparent app.db.collection(name) / app.db.model(name)
- Purpose: Keep Vext handlers and services on the native
app.dbmental model while applying permission-core data checks inside protected requests. - Parameters: Enabled by
data.transparent: true; route protection must have resolved a permission context for the current request. - State impact: During plugin setup the app-level
dbextension is wrapped. Inside protected requests,collection()and basicmodel()CRUD are routed through the current request's authorized data API. Outside protected requests, public routes and background jobs continue using the host DB. - Raw return: The host's normal DB access outside protected requests; an authorized collection/model facade inside protected requests.
app.db.use(...), app.db.pool(...), model raw(), aggregate(), watch(), and collection/index management fail closed inside protected requests rather than bypassing authorization.
req.auth.permission.filterResponse(apiResource, payload, context?)
- Purpose: Project a response payload inside a Vext handler according to the current user's response-field grants.
- Parameters:
apiResourceisapi:METHOD:/path;payloadis the data about to be returned;contextis optional. - State impact: Read-only; it first checks whether the current subject can
invokethe API. - Raw return:
SubjectRuntimeResult<unknown>with projected data indata.
appExtensions.permission
- Purpose: Declare
app.permission: PermissionCorefor Vext's type system. - Parameters: No runtime parameters.
- State impact: The actual app extension value is installed by plugin setup.
- Raw return: This is a type extension definition; application code reads core through
app.permission.
Responses and side effects
Plugin setup initializes PermissionCore, installs route guards, binds req.auth.permission, optionally wraps app.db, optionally binds req.monsqlize or req.db, exposes app.permission, and registers close hooks. Routes protected by routes.protect or route-level permission check invoke + api:METHOD:/path before the handler. If the handler uses res.json(), the plugin projects the response according to response-field config and sets Cache-Control: private, no-store.
Failures and limits
Common errors include VEXT_MONSQLIZE_REQUIRED, VEXT_MONSQLIZE_INCOMPATIBLE, VEXT_AUTH_REQUIRED, VEXT_APP_EXTENSION_CONFLICT, VEXT_AUTH_EXTENSION_CONFLICT, VEXT_ROUTE_PERMISSION_INVALID, VEXT_ROUTE_RESTART_REQUIRED, and DATA_OPERATION_UNSUPPORTED. Missing data.scopeFields.tenantId, invalid routes.protect/public, invalid data.transparent, invalid data.exposeAs, too many collection overrides, occupied request aliases, or an unwrappable app.db fail closed during startup. Route permission requirements are limited to 32; route default patterns are limited to 128. Route changes after startup require a cold restart. Protected routes with caching enabled refuse startup unless route cache is explicitly disabled.
Example
This example does not configure data.collections. app.db.collection('orders') and req.auth.permission.data.collection('orders') both use the host orders collection by default and derive db:orders; configure data.collections only when a physical collection name or scope mapping needs an override.
Related
See Vext Plugin, Authentication Boundary, Authorized Collection API, Configure APIs and Response Fields API, and the runnable Vext example.