Blazing Fast, Zero dependency, Hierarchical Role-Based Access Control for Node.js
npm install @rbac/rbac




* ⏱ Lightweight
* ⚡️Fastest RBAC (check benchmarks)
* ️🍃low dependency
* Focused on operations
* Scalable
* Each role is given specific access rights for every operation
* High granularity in assigning rights
* Wildcard and regex support for operations
* Optional database adapters (MongoDB, MySQL, PostgreSQL)
* Express, NestJS and Fastify middlewares
* Roles can be updated at runtime
This project now uses Vite to generate the bundled output
Thanks to Karl Düüna (DeadAlready) and his awesome post on medium
#### Install
yarn add @rbac/rbac or npm install @rbac/rbac
This library is written in TypeScript and the published package ships with
its declaration files for a great developer experience.
#### Import
The package supports both ES modules and CommonJS.
ES Modules (recommended)
``ts
// Default import
import RBAC from '@rbac/rbac';
// Named imports
import { createTenantRBAC, MongoRoleAdapter } from '@rbac/rbac';
// Combined
import RBAC, { createTenantRBAC } from '@rbac/rbac';
`
CommonJS
`js
// Default import
const RBAC = require('@rbac/rbac');
// Named imports
const { createTenantRBAC, MongoRoleAdapter } = require('@rbac/rbac');
// Combined
const RBAC = require('@rbac/rbac');
const { createTenantRBAC } = require('@rbac/rbac');
`
#### Usage
RBAC is a curried function thats initially takes an object with configurations,
then returns another function that takes an object with roles,
finally returns an object that holds "can" property that is a function.
You can use it in many ways, below is one of them:
#### Setup RBAC config
!step 01
| Property | Type | Params | Default | Description |
|-------------- |--------------- |------------------------------------------------------------- |--------------- |----------------------------------------- |
| logger | Function | role: String
operation: String
result: Boolean
colorsEnabled: Boolean (optional) | defaultLogger | Function that logs operations to console |
| enableLogger | Boolean | | true | Enable or disable logger |
| colors | Boolean | | auto-detect | Enable, disable, or auto-detect color support in logger output |
#### Creating some roles
!step 02
RBAC expects an object with roles as property names.
| Property | Type | Example | Description |
|---------- |-------------- |------------------------------------------------ |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| can | Array | `['products:*']` | Array of strings, list of operations that user can do, it also supports glob patterns |`
| when | Function, Async Function or Promise | (params , done ) => done (null , true )` | Optional Promise that should resolve in Truthy or Falsy, an async function that returns a boolean or Promise, or a Callback function that receives params and done as properties, should return done passing errors and result |`
| inherits | Array | ['user']` | Optional Array of strings, list of roles inherited by this role |
###### IMPORTANT! "when" property can be a Callback function that receives params and done, an async function that returns a boolean or Promise, or a Promise that resolves in Truthy or Falsy values. Example:
`ts
import type { Roles } from '@rbac/rbac';
interface Params {
registered: boolean;
}
const roles: Roles
supervisor: {
can: [{ name: 'products:find', when: (params, done) => {
// done receives error as first argument and Truthy or Falsy value as second argument
done(null, params.registered);
}}]
},
admin: {
can: [{ name: 'products:*', when: async (params) => {
return params.registered;
} }]
}
};
`
#### Check if user can do some operation
!step 03
| Param | Type | Example | Description |
|-------- |----------------------------------------------- |-------------------------- |---------------------------------------------------------------- |
| First | String | `'admin'` | Array of strings, list of operations that user can do |`
| Second | String, Glob (Wildcard), Regex | 'products:find'` | Operation to validate |`
| Third | Any | {registered: true}` | Optional Params that will flow to "when" callback Function |$3
RBAC exposes two helpers to modify the role definition at runtime. addRole adds a new role and updateRoles merges new definitions with the existing ones.
`ts
import RBAC from '@rbac/rbac'
const base = RBAC({ enableLogger: false })({
user: { can: ['products:find'] }
})
base.addRole('editor', { can: ['products:update'], inherits: ['user'] })
await base.can('editor', 'products:update') // true
base.updateRoles({
user: { can: ['products:find', 'products:create'] }
})
await base.can('user', 'products:create') // true
`
The default logger automatically detects color support in your terminal and applies ANSI color codes accordingly. You can also manually control color output:
`ts
import RBAC from '@rbac/rbac'
// Auto-detect color support (default behavior)
const rbacAuto = RBAC({ enableLogger: true })({
user: { can: ['products:find'] }
})
// Force colors enabled
const rbacWithColors = RBAC({ enableLogger: true, colors: true })({
user: { can: ['products:find'] }
})
// Force colors disabled (plain text output)
const rbacNoColors = RBAC({ enableLogger: true, colors: false })({
user: { can: ['products:find'] }
})
`
Color Detection Logic:
1. If colors: true is set, colors are always enabledcolors: false
2. If is set, colors are always disabledcolors
3. If is not specified (default), the logger automatically detects:FORCE_COLOR
- environment variable (enables colors)NO_COLOR
- environment variable (disables colors)process.stdout.isTTY
- TTY detection ()
- CI environment detection (GitHub Actions, GitLab CI, CircleCI)
This ensures readable logs across all environments including CI systems, Windows terminals, and redirected output.
RBAC exposes optional adapters to load and persist role definitions using
MongoDB, MySQL or PostgreSQL. Each adapter implements the RoleAdaptergetRoles
interface with , addRole and updateRoles methods.
`ts
import RBAC from '@rbac/rbac'
import { MongoRoleAdapter } from '@rbac/rbac/adapters'
const adapter = new MongoRoleAdapter({
uri: 'mongodb://localhost:27017',
dbName: 'mydb',
collection: 'roles'
})
const roles = await adapter.getRoles()
const rbac = RBAC()(roles)
`
Adapters available:
- MongoRoleAdapterMySQLRoleAdapter
- PostgresRoleAdapter
-
Adapters also allow customizing the underlying table or collection column names
through a columns option when creating a new instance:
`ts`
const adapter = new MySQLRoleAdapter({
table: 'roles',
columns: { name: 'rname', role: 'rdef', tenantId: 'tid' }
})
Adapters can optionally receive a tenantId parameter to store and retrievecreateTenantRBAC
roles for different tenants. When omitted, the adapter falls back to a default
tenant so existing single-tenant usage keeps working. Use to
instantiate an RBAC instance scoped to a tenant:
`ts
import { MongoRoleAdapter, createTenantRBAC } from '@rbac/rbac';
const adapter = new MongoRoleAdapter({ uri: 'mongodb://localhost:27017', dbName: 'mydb', collection: 'roles' });
await adapter.addRole('user', { can: ['products:find'] }, 'tenant-a');
const rbacTenantA = await createTenantRBAC(adapter, 'tenant-a');
await rbacTenantA.can('user', 'products:find'); // true
`
Want more? Check out the examples folder.
RBAC also provides helper middlewares for Express, NestJS and Fastify.
They make it easy to guard routes using existing role definitions.
`ts
import RBAC, { createExpressMiddleware } from '@rbac/rbac';
const rbac = RBAC({ enableLogger: false })({
user: { can: ['products:find'] }
});
const canFindProducts = createExpressMiddleware(rbac)('products:find');
app.get('/products', canFindProducts, handler);
`
For NestJS and Fastify you can use createNestMiddleware and createFastifyMiddleware
respectively with a similar API.
- [X] Wildcard support
- [X] Regex support
- [X] Update roles in runtime
- [X] Async when callbacks
- [X] Database adapters (MongoDB, MySQL, PostgreSQL)
- [X] Middlewares for Express, NestJS and Fastify
- Rewritten in TypeScript
- Internal refactor focused on readability and performance
- Added support to update roles at runtime
- Database adapters
- Middlewares for Express, NestJS and Fastify
Run npm run bench to execute the performance suite. The script runs two end-to-end scenarios:
- Baseline comparison – compares @rbac/rbac with AccessControl, RBAC, Easy RBAC and Fast RBAC using the default dataset.when
- Large dataset comparison – stresses the libraries with hundreds of resources, deep inheritance chains and three flavours (callback, async function and promise).
For each scenario the suite generates detailed reports (JSON/CSV/HTML chart) under benchmarks/results/ and prints a human-readable summary (ops/sec, margins, standard deviation, samples, etc.).
``
$ npm run bench
RBAC Performance Comparison ops/sec: 6859456, 6193737, 4427263, ...
RBAC Performance Comparison - Large Dataset ops/sec: 3277352, 3396327, 3424089, ...
The baseline run shows @rbac/rbac leading all categories; the large dataset confirms the same behaviour when conditional checks and large permission sets come into play.
- Migration guide from v1 to v2
- Changelog
#### Contributions are welcome!
1. Build RBAC
* Run npm install (or yarn install) to get RBAC's dependenciesnpm run build
* Run to compile the library and produce the minified bundle using Vite
2. Development mode
* Having all the dependencies installed run yarn dev. This command will generate a non-minified version of your library and will run a watcher so you get the compilation on file change.
3. Running the tests
* Run yarn test
4. Scripts
* npm run build - produces production version of your library under the lib folder and generates lib/@rbac/rbac.min.js via Vitenpm run dev
* - produces development version of your library and runs a watchernpm test
* - well ... it runs the tests :)npm run test:watch
* - same as above but in a watch modenpm run bench` - run the benchmark suite
*
This project is under MIT License [https://opensource.org/licenses/MIT]