Skip to content

Getting Started

Terminal window
npm i accesscontrol

AccessControl v3 is ESM and ships TypeScript types.

import { AccessControl } from 'accesscontrol';

Because v3 is pure ESM with no Node-only built-in dependencies, it runs beyond Node.js (≥ 20):

  • Bun — works out of the box; install and import exactly as above:
    Terminal window
    bun add accesscontrol
    import { AccessControl } from 'accesscontrol';
  • Deno — import via the npm: specifier (no install step):
    import { AccessControl } from 'npm:accesscontrol';
    or pin a version: npm:accesscontrol@^3. If you keep an import map, add "accesscontrol": "npm:accesscontrol@^3" and import { AccessControl } from 'accesscontrol'.

The API is identical across runtimes — only the install/import line differs.

Create roles with grant() / deny(), chaining as you go. Roles inherit others with extend(); grants are additive and an explicit deny always wins.

const ac = new AccessControl();
ac.grant('user')
.createOwn('video')
.deleteOwn('video')
.readAny('video')
.grant('admin')
.extend('user')
.updateAny('video', ['title']) // only the `title` attribute
.deleteAny('video');
ac.can('user').createOwn('video').granted; // true
ac.can('admin').updateAny('video').attributes; // ['title']

Use filter() to strip a record down to the allowed attributes:

const perm = ac.can('user').readAny('video');
perm.filter(videoRecord); // only the granted fields

Constrain a grant with .where(), and supply per-check data via the context.

ac.grant('manager').where('$.order.value <= 100000').updateAny('order', ['*']);
ac.can('manager', { order: { value: 5000 } }).updateAny('order').granted; // true

Tell the engine how ownership is determined and pass the record — own checks are then enforced:

const ac = new AccessControl({}, { policy: { ownerField: 'ownerId' } });
ac.grant('user').updateOwn('order', ['*']);
ac.can('user', { user: { id: 7 }, order: { ownerId: 7 } }).updateOwn('order').granted; // true
ac.can('user', { user: { id: 7 }, order: { ownerId: 9 } }).updateOwn('order').granted; // false